2010-11-16 3 views
9

Was ist der beste Weg, um binäre Bits (es könnte eine Liste von 0/1, zum Beispiel) in reversible Weise in Zahlen umzuwandeln. Ich habe ein natives Prädikat in swi geschrieben, aber gibt es eine bessere Lösung? Mit freundlichen Grüßenreversibel "binär zu Nummer" Prädikat

+0

Was die Antwort für die folgende Abfrage sein sollte: 'binary_number (B, -5) .': eine Ausnahme wie * Domain-Fehler: \ 'not_less_than_zero 'erwartet, gefunden \' -5 '* ** oder ** Fehler (' no'/'false')? –

+0

@TudorBerariu: Wie du willst. Beide Fehler und ein Fehler ist in Ordnung. (BTW; ich habe deine Frage vorher nicht gelesen, du musst @ mich) – false

Antwort

10

Verwendung CLP (FD) Einschränkungen, zum Beispiel:

:- use_module(library(clpfd)). 

binary_number(Bs0, N) :- 
     reverse(Bs0, Bs), 
     foldl(binary_number_, Bs, 0-0, _-N). 

binary_number_(B, I0-N0, I-N) :- 
     B in 0..1, 
     N #= N0 + B*2^I0, 
     I #= I0 + 1. 

Beispielabfragen:

?- binary_number([1,0,1], N). 
N = 5. 

?- binary_number(Bs, 5). 
Bs = [1, 0, 1] . 

?- binary_number(Bs, N). 
Bs = [], 
N = 0 ; 
Bs = [N], 
N in 0..1 ; 
etc. 
+1

+1, elegante Lösung. –

+1

+1 sehr schlau! – sharky

+1

'binary_number (Bs, 5) .' wird nicht beendet. – false

3

Die Lösung

Diese Antwort ein Prädikat zu schaffen sucht binary_number/2 das zeigt sowohl als auch die besten Abschlusseigenschaften. Ich habe when/2 verwendet, um Abfragen wie canonical_binary_number(B, 10) davon abzuhalten, in Endlosschleife zu gehen, nachdem ich die erste (einzigartige) Lösung gefunden habe. Es gibt einen Kompromiss, natürlich hat das Programm redundante Ziele jetzt.

canonical_binary_number([0], 0). 
canonical_binary_number([1], 1). 
canonical_binary_number([1|Bits], Number):- 
    when(ground(Number), 
     (Number > 1, 
      Pow is floor(log(Number)/log(2)), 
      Number1 is Number - 2^Pow, 
      ( Number1 > 1 
      -> Pow1 is floor(log(Number1)/log(2)) + 1 
      ; Pow1 = 1 
     ))), 
    length(Bits, Pow), 
    between(1, Pow, Pow1), 
    length(Bits1, Pow1), 
    append(Zeros, Bits1, Bits), 
    maplist(=(0), Zeros), 
    canonical_binary_number(Bits1, Number1), 
    Number is Number1 + 2^Pow. 

binary_number(Bits, Number):- 
    canonical_binary_number(Bits, Number). 
binary_number([0|Bits], Number):- 
    binary_number(Bits, Number). 

Reinheit und Beendigung

ich behaupten, dass dieses Prädikat präsentiert von der Konstruktion. Ich hoffe, ich habe es richtig aus diesen Antworten: one, two und three.

Jedes Ziel mit geeigneten Argumenten wird beendet. Wenn Argumente geprüft werden müssen, auf die einfachste Art und Weise zu erreichen ist dies mit dem eingebauten in length/2:

binary_number(Bits, Number):- 
    length(_, Number), 
    canonical_binary_number(Bits, Number). 

?- binary_number(Bits, 2+3). 
ERROR: length/2: Type error: `integer' expected, found `2+3' 
    Exception: (6) binary_number(_G1642009, 2+3) ? abort 
% Execution Aborted 
?- binary_number(Bits, -1). 
ERROR: length/2: Domain error: `not_less_than_zero' expected, found `-1' 
    Exception: (6) binary_number(_G1642996, -1) ? creep 

Beispiel fragt

?- binary_number([1,0,1|Tail], N). 
Tail = [], 
N = 5 ; 
Tail = [0], 
N = 10 ; 
Tail = [1], 
N = 11 ; 
Tail = [0, 0], 
N = 20 . 

?- binary_number(Bits, 20). 
Bits = [1, 0, 1, 0, 0] ; 
Bits = [0, 1, 0, 1, 0, 0] ; 
Bits = [0, 0, 1, 0, 1, 0, 0] ; 
Bits = [0, 0, 0, 1, 0, 1, 0, 0] ; 
Bits = [0, 0, 0, 0, 1, 0, 1, 0, 0] . 

?- binary_number(Bits, N). 
Bits = [0], 
N = 0 ; 
Bits = [1], 
N = 1 ; 
Bits = [1, 0], 
N = 2 ; 
Bits = [1, 1], 
N = 3 ; 
Bits = [1, 0, 0], 
N = 4 ; 
Bits = [1, 0, 1], 
N = 5 . 
+0

'binary_number (L, -1)' Schleifen – false

+0

Es ist außerhalb der Domäne des zweiten Arguments. Ich kann einige Bedingungen hinzufügen wie 'length (_, Number)' und Ausnahmen werden ausgelöst. –

+0

.... oder füge 'when (ground (Number), Number> = 0)' zu 'binary_predicate/2' hinzu. –

1

mit Bits spielen ...

binary_number(Bs, N) :- 
    var(N) -> foldl(shift, Bs, 0, N) ; bitgen(N, Rs), reverse(Rs, Bs). 

shift(B, C, R) :- 
    R is (C << 1) + B. 

bitgen(N, [B|Bs]) :- 
    B is N /\ 1 , (N > 1 -> M is N >> 1, bitgen(M, Bs) ; Bs = []). 
7

Hier ist die Lösung, an die ich gedacht habe, oder eher, was ich erhoffte, existiert.

:- use_module(library(clpfd)). 

binary_number(Bs, N) :- 
    binary_number_min(Bs, 0,N, N). 

binary_number_min([], N,N, _M). 
binary_number_min([B|Bs], N0,N, M) :- 
    B in 0..1, 
    N1 #= B+2*N0, 
    M #>= N1, 
    binary_number_min(Bs, N1,N, M). 

Diese Lösung endet auch für Anfragen wie:

?- Bs = [1|_], N #=< 5, binary_number(Bs, N). 
+1

Dies ist ein eleganter, einfacher Weg um das Terminierungsproblem (+1) zu umgehen und vermeidet die Potenzierung (:)). – lurker

+0

Ich verstehe nicht den Zweck von 'M'. Kannst du es nicht entfernen und ersetzen es in 'M #> = N1' durch' N'? – Fatalize

+0

@Fatalize: 'M', daher wird das 4. Argument benötigt, um sicherzustellen, dass das Prädikat wirklich reversibel ist. Es ist die ursprüngliche Variable ... – false