Anzahl unterschiedlicher nicht leerer Teilsequenzen der binären Expansion

19

Eine Untersequenz ist eine beliebige Sequenz, die Sie durch Löschen einer beliebigen Anzahl von Zeichen von einer anderen abrufen können. Die unterschiedlichen nicht-leeren Teilsequenzen 100sind 0, 1, 00, 10, 100. Die unterschiedlichen nicht-leeren Teilsequenzen 1010sind 0, 1, 00, 01, 10, 11, 010, 100, 101, 110, 1010.

Schreiben Sie ein Programm oder eine Funktion, die bei einer positiven Ganzzahl n die Anzahl der eindeutigen nicht leeren Teilsequenzen der binären Erweiterung von n zurückgibt .

Beispiel: da 4ist 100in binär, und wir haben gesehen, dass es oben fünf verschiedene nicht leere Untersequenzen hatte, also f(4) = 5. Ab n = 1 beginnt die Sequenz:

1, 3, 2, 5, 6, 5, 3, 7, 10, 11, 9, 8, 9, 7, 4, 9, 14, 17, 15, 16, 19, 17, 12

Ihr Programm muss jedoch auf jedem modernen Computer in weniger als einer Sekunde für n <2 50 funktionieren . Einige große Beispiele:

f(1099511627775) = 40
f(1099511627776) = 81
f(911188917558917) = 728765543
f(109260951837875) = 447464738
f(43765644099) = 5941674
orlp
quelle
4
Ich bin mit der zeitlichen Beschränkung nicht einverstanden.
ATaco
1
Das kam mir sehr bekannt vor, vor allem nachdem ich mir die Handlung angesehen hatte. Es stellte sich heraus, dass ich mir eine sehr eng verwandte Sequenz angesehen habe Anfang dieses Jahres habe, aber ich habe die Anzahl der eindeutigen Binärzahlen und nicht der Binärzeichenfolgen gezählt, die Sie erhalten, wenn Sie Teilsequenzen nehmen (daher habe ich führende Nullen abgezinst). Ich hatte es sogar in einer Sandbox abgelegt, aber aufgrund der Entsprechung im Math.SE-Post wäre es ein Betrug einer Stern-Brocot-Herausforderung gewesen. Die Handlung Ihrer Sequenz ist allerdings ein bisschen schöner (dh chaotischer). :)
Martin Ender
5
@ATaco Die zeitliche Beschränkung hat einen guten Grund. Es gibt einen effizienten Algorithmus, der interessant und dennoch gut golfbar ist. Wenn ich keine zeitliche Beschränkung hätte, würde fast jede Antwort einfach alle möglichen Teilsequenzen erzwingen, was sehr, sehr schnell nicht mehr funktionieren würde. In gewisser Weise sind sie keine Antworten.
Orlp

Antworten:

10

Python 3 , 95 Bytes 83 Bytes

[-12 Bytes dank Mr.XCoder :)]

def f(x):
 v=[2,1];c=1
 for i in bin(x)[3:]:k=int(i);c+=v[k];v[1-k]+=v[k]
 return c

Probieren Sie es online!

Eine Anmerkung zum Algorithmus. Der Algorithmus berechnet das Inkrement in eindeutigen Teilsequenzen, die durch das Bit an einer gegebenen Position t gegeben sind. Das Inkrement für das erste Bit ist immer 1. Der Algorithmus durchläuft dann die Folge der Bits s (t) und addiert das Inkrement v [s (t)]. Bei jedem Schritt wird das Inkrement für das Komplement von s (t), v [1 - s (t)] auf v [1] + v [0] aktualisiert. Die letzte Zahl ist die Summe aller Inkremente.

Es sollte in O (log2 (n)) ausgeführt werden, wobei n die Eingabenummer ist.

NofP
quelle
1
83 Bytes oder 83 Bytes
Mr. Xcoder
8

JavaScript (ES6), 53 51 Bytes

f=(n,r=~(a=[]))=>n<1?~r:f(n/2,r*2-~~a[n&=1],a[n]=r)

Testfälle

Formatiert und kommentiert

f = (                      // f is a recursive function taking:
  n,                       //   n = integer
  r = ~(                   //   r = last result, initially set to -1
    a = []                 //   and using a[] = last results for 0 and 1,
  )                        //   implicitly initialized to [0, 0]
) =>                       //
  n < 1 ?                  // if n is less than 1:
    ~r                     //   we're done: return -(r + 1)
  :                        // else:
    f(                     //   do a recursive call with:
      n / 2,               //     n / 2
      r * 2 - ~~a[n &= 1], //     updated result = r * 2 - last result for this binary digit
      a[n] = r             //     update last result for this binary digit
    )                      //   end of recursive call

Nicht rekursive Version, 63 Bytes

3 Bytes gespart dank @ThePirateBay

s=>[...s.toString(2)].map(l=c=>l[p=r,r=r*2-~~l[c],c]=p,r=1)|r-1

Testfälle

Arnauld
quelle
Ich denke, Sie können 3 Bytes sparen, indem Sie mapder Flag-Variablen lanstelle eines leeren Arrays die innere Funktion (das erste Argument von ) zuweisen .
@ThePirateBay Schöne. Vielen Dank!
Arnauld
6

Gelee , 10 Bytes

B3;BSṛ¦/’S

Dies nutzt die Verbesserung von @ xnor gegenüber dem @ NofP-Algorithmus .

Probieren Sie es online!

Hintergrund

Sei (a 1 , ..., a n ) eine endliche binäre Folge. Definieren Sie für jede nicht negative ganze Zahl k ≤ n o k als die Anzahl der eindeutigen Teilfolgen von (a 1 , ..., a k ) , die entweder leer sind oder mit 1 enden , z k als die Anzahl der eindeutigen Teilfolgen, die leer sind entweder leer oder mit 0 enden .

Es ist klar, o 0 = z 0 = 1 , da die einzige Untersequenz der leeren Sequenz die leere Sequenz ist.

Für jeden Index k die Gesamtzahl von Untersequenzen von (a 1 , ..., a k ) ist o k + z k - 1 (Subtrahieren 1 trägt der Tatsache Rechnung , dass beide o k und Z k Zählung der leere Sequenz). Die Gesamtzahl der nicht-leerer Subsequenzen ist daher o k + z k - 2 . Die Herausforderung besteht darin, o n + z n - 2 zu berechnen .

Jedes Mal , wenn k> 0 , können wir berechnen o k und z k rekursiv. Es gibt zwei Fälle:

  • a k = 1

    z k = z k-1 , da (a 1 , ..., a k-1 ) und (a 1 , ..., a k-1 , 1) die gleichen Teilfolgen haben, die mit 0 enden .

    Für jede der o k - 1 nicht-leeren Subsequenzen von (a 1 , ..., a k ) dieses Ende in 1 , können wir den nachlauf entfernen 1 zu erhalten , eine des o k-1 + z k-1 - 1 Teilfolgen (a 1 , ..., a k-1 ) . Umgekehrt führt das Anhängen einer 1 an jede der letzteren o k-1 + z k-1 - 1 Sequenzen zu einer der o k- 1 früheren Sequenzen. Somit ist o k - 1 = ok-1 + zk-1 - 1 und o k = o k-1 + z k-1 .

  • a k = 0

    Ähnlich wie im vorherigen Fall erhalten wir die rekursiven Formeln o k = o k-1 und z k = z k-1 + o k-1 .

Wie es funktioniert

B3;BSṛ¦/’S  Main link. Argument: n (positive integer)

B           Binary; convert n to base 2.
 3;         Prepend a 3.
   B        Binary; convert all integers in the resulting array to base 2, mapping
            0 to [0], 1 to [1], and the prepended 3 to [1, 1].
       /    Reduce the resulting array by the quicklink to the left, which will be 
            called with left argument [x, y] (integer pair) and right argument [j] 
            (either [0] or [1]).
      ¦     Sparse application.
    S           Compute the sum (x + y) and...
     ṛ          for each index in the right argument (i.e., for j)...
            replace the element of [x, y] at that index with (x + y).
       ’    Decrement both integers in the resulting pair.
        S   Take the sum.
Dennis
quelle
Hey, Dennis, würde es dir etwas ausmachen, eine kurze Erklärung hinzuzufügen, warum der Algorithmus funktioniert?
Jonah
Ich habe eine Erklärung hinzugefügt.
Dennis
4

05AB1E , 12 Bytes

0¸sbvDO>yǝ}O

Probieren Sie es online! Erläuterung: Wie in den anderen Antworten angegeben, ist die Anzahl der Teilfolgen für eine Binärzeichenfolge a..y0, die mit einer 1 endet, dieselbe wie für die Binärzeichenfolge a..y, während die Anzahl, die mit einer 1 endet, 0die Gesamtanzahl der Teilfolgen für die Binärzeichenfolge ist Zeichenfolge a..y(die jeweils ein 0Suffix erhalten) plus eins für 0sich. Im Gegensatz zu den anderen Antworten schließe ich die leere Untersequenz nicht ein, da dies ein Byte erspart, das den Anfangszustand aufbaut.

0¸s             Push [0] under the input
   b            Convert the input to binary
    v     }     Loop over the digits
     D          Duplicate the array
      O         Take the sum
       >        Increment
        yǝ      Replace the index corresponding to the binary digit
           O    Take the sum of the final array
Neil
quelle
1

Java 8, 97 Bytes

n->f(n,1,1)long f(long n,long a,long b){return n>0?f(n/2,a+Math.floorMod(~n,2)*b,n%2*a+b):a+b-2;}

Port von @xnors Python 2-Antwort , was wiederum eine Verbesserung von @NofPs Python 3-Antwort darstellt .

Probieren Sie es hier aus.


Vielleicht ist es eine gute Sache, dass der Tag vorhanden war, weil ich anfangs Folgendes hatte, um alle Teilsequenzen zu brutalisieren:

import java.util.*;n->p(n.toString(n,2)).size()-1;Set p(String s){Set r=new HashSet();r.add("");if(s.isEmpty())return r;Set q=p(s.substring(1));r.addAll(q);for(Object o:q)r.add(""+s.charAt(0)+o);return r;}

Probieren Sie es hier aus.

Das hat auch funktioniert, hat aber für die letzten drei Testfälle viel zu lange gedauert. Ganz zu schweigen davon, dass es viel länger ist ( 208 bis 204 Bytes ).

Kevin Cruijssen
quelle
1

6502 Maschinencode (C64), 321 Bytes

00 C0 20 FD AE A2 00 9D 4F C1 E8 20 73 00 90 F7 9D 4F C1 A0 FF C8 B9 4F C1 D0
FA A2 15 CA 88 30 0A B9 4F C1 29 0F 9D 4F C1 10 F2 A9 00 9D 4F C1 CA 10 F8 A9
00 A0 07 99 64 C1 88 10 FA A0 40 A2 6C 18 BD E4 C0 90 02 09 10 4A 9D E4 C0 E8
10 F2 A2 07 7E 64 C1 CA 10 FA 88 F0 13 A2 13 BD 50 C1 C9 08 30 05 E9 03 9D 50
C1 CA 10 F1 30 D1 A2 0F A9 00 9D 3F C1 CA D0 FA A9 01 8D 3F C1 8D 47 C1 A2 08
CA BD 64 C1 F0 FA A0 09 1E 64 C1 88 90 FA B0 0A CA 30 28 A0 08 1E 64 C1 90 04
A9 47 B0 02 A9 4F 8D AF C0 86 FE A2 F8 18 BD 47 C0 7D 4F C0 9D 47 C0 E8 D0 F4
A6 FE 88 D0 DC F0 D5 A2 F8 BD 47 C0 7D 4F C0 9D 6C C0 E8 D0 F4 AD 64 C1 E9 01
8D 64 C1 A2 F9 BD 6C C0 E9 00 9D 6C C0 E8 D0 F5 A0 15 A9 00 99 4E C1 88 D0 FA
A0 40 A2 13 BD 50 C1 C9 05 30 05 69 02 9D 50 C1 CA 10 F1 0E 64 C1 A2 F9 3E 6C
C0 E8 D0 FA A2 13 BD 50 C1 2A C9 10 29 0F 9D 50 C1 CA 10 F2 88 D0 D1 E0 14 F0
06 E8 BD 4F C1 F0 F6 09 30 99 4F C1 C8 E8 E0 15 F0 05 BD 4F C1 90 F0 A9 00 99
4F C1 A9 4F A0 C1 4C 1E AB

Online-Demo

Online-Demo mit Fehlerprüfung (346 Bytes)

Verwendung: sys49152,[n] zBsys49152,911188917558917 .

Die zeitliche Beschränkung und die Testfälle erfordern Lösungen für die Berechnung von 64-Bit-Zahlen. Daher ist es an der Zeit, zu beweisen, dass der C64 als " moderne Maschine" eingestuft ist " qualifiziert ist;)

Dies erfordert natürlich einiges an Code, das Betriebssystem bietet nichts für ganze Zahlen, die breiter als 16 Bit sind . Der lahme Teil hier: Es ist eine weitere Implementierung (leicht modifiziert) des NofP-Algorithmus resp. xnors verbesserte Variante . Danke für die Idee;)


Erläuterung

Hier ist eine kommentierte Demontage-Liste des relevanten Teils, der den Algorithmus ausführt:

.C:c06c  A2 0F       LDX #$0F           ; 15 bytes to clear
.C:c06e  A9 00       LDA #$00
.C:c070   .clearloop:
.C:c070  9D 3F C1    STA .num_a,X
.C:c073  CA          DEX
.C:c074  D0 FA       BNE .clearloop
.C:c076  A9 01       LDA #$01           ; initialize num_a and num_b
.C:c078  8D 3F C1    STA .num_a         ; to 1
.C:c07b  8D 47 C1    STA .num_b
.C:c07e  A2 08       LDX #$08           ; 8 bytes of input to check,
.C:c080   .findmsb:                     ; start at most significant
.C:c080  CA          DEX
.C:c081  BD 64 C1    LDA .nc_num,X
.C:c084  F0 FA       BEQ .findmsb       ; repeat until non-0 byte found
.C:c086  A0 09       LDY #$09           ; 8 bits to check (+1 for pre dec)
.C:c088   .findbit:
.C:c088  1E 64 C1    ASL .nc_num,X      ; shift left, highest bit to carry
.C:c08b  88          DEY
.C:c08c  90 FA       BCC .findbit       ; bit was zero -> repeat
.C:c08e  B0 0A       BCS .loopentry     ; jump into calculation loop
.C:c090   .mainloop:
.C:c090  CA          DEX                ; next byte
.C:c091  30 28       BMI .done          ; index -1? -> done calculating
.C:c093  A0 08       LDY #$08           ; 8 bits to check
.C:c095   .bitloop:
.C:c095  1E 64 C1    ASL .nc_num,X      ; shift left, highest bit to carry
.C:c098  90 04       BCC .tgt_b         ; if 0, store addition result in num_b
.C:c09a   .loopentry:
.C:c09a  A9 47       LDA #$47
.C:c09c  B0 02       BCS .tgt_a         ; ... else store in num_a ...
.C:c09e   .tgt_b:
.C:c09e  A9 4F       LDA #$4F
.C:c0a0   .tgt_a:
.C:c0a0  8D AF C0    STA $C0AF          ; ... using self-modification.
.C:c0a3  86 FE       STX $FE            ; save byte index
.C:c0a5  A2 F8       LDX #$F8           ; index for adding
.C:c0a7  18          CLC
.C:c0a8   .addloop:
.C:c0a8  BD 47 C0    LDA $C047,X        ; load byte from num_a
.C:c0ab  7D 4F C0    ADC $C04F,X        ; add byte from num_b
.C:c0ae  9D 47 C0    STA $C047,X        ; store to num_a or num_b
.C:c0b1  E8          INX                ; next index
.C:c0b2  D0 F4       BNE .addloop       ; done if index overflown
.C:c0b4  A6 FE       LDX $FE            ; restore byte index
.C:c0b6  88          DEY                ; decrement bit index
.C:c0b7  D0 DC       BNE .bitloop       ; bits left in current byte -> repeat
.C:c0b9  F0 D5       BEQ .mainloop      ; else repeat main loop
.C:c0bb   .done:
.C:c0bb  A2 F8       LDX #$F8           ; index for adding
.C:c0bd   .addloop2:
.C:c0bd  BD 47 C0    LDA $C047,X        ; load byte from num_a
.C:c0c0  7D 4F C0    ADC $C04F,X        ; add byte from num_b
.C:c0c3  9D 6C C0    STA $C06C,X        ; store to nc_num (result)
.C:c0c6  E8          INX                ; next index
.C:c0c7  D0 F4       BNE .addloop2      ; done if index overflown
.C:c0c9  AD 64 C1    LDA .nc_num        ; load least significant result byte
.C:c0cc  E9 01       SBC #$01           ; subtract 2 (1 + negated carry)
.C:c0ce  8D 64 C1    STA .nc_num        ; store least significant result byte
.C:c0d1  A2 F9       LDX #$F9           ; index for subtract
.C:c0d3   .subloop:
.C:c0d3  BD 6C C0    LDA $C06C,X        ; subtract 0 from all other bytes
.C:c0d6  E9 00       SBC #$00           ; for handling carry if necessary
.C:c0d8  9D 6C C0    STA $C06C,X
.C:c0db  E8          INX
.C:c0dc  D0 F5       BNE .subloop       

Der Rest ist Eingabe / Ausgabe und Konvertieren zwischen Zeichenfolge und 64-Bit-Ganzzahl ohne Vorzeichen (Little-Endian) unter Verwendung eines Double-Dabble-Algorithmus. Falls Sie interessiert sind, finden Sie hier die gesamte Assembly-Quelle für die Version mit Fehlerprüfung - die "Golfed" -Version befindet sich im Zweig "Golf".

Felix Palmen
quelle