Số lượng các chuỗi không trống khác biệt của mở rộng nhị phân


19

Một chuỗi là bất kỳ chuỗi nào bạn có thể nhận được từ một chuỗi khác bằng cách xóa bất kỳ số lượng ký tự. Các biệt subsequences không trống của 1000, 1, 00, 10, 100. Các subsequences không trống riêng biệt của 10100, 1, 00, 01, 10, 11, 010, 100, 101, 110, 1010.

Viết chương trình hoặc hàm cho số nguyên dương n trả về số lượng các chuỗi không trống khác biệt của khai triển nhị phân của n .

Ví dụ: vì 4100ở dạng nhị phân, và chúng tôi đã thấy rằng nó có năm chuỗi không trống khác biệt ở trên, vì vậy f(4) = 5. Bắt đầu từ n = 1 , chuỗi bắt đầu:

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

Tuy nhiên, chương trình của bạn phải hoạt động cho mọi n <2 50 dưới một giây trên bất kỳ máy hiện đại nào. Một số ví dụ lớn:

f(1099511627775) = 40
f(1099511627776) = 81
f(911188917558917) = 728765543
f(109260951837875) = 447464738
f(43765644099) = 5941674

4
Tôi không đồng ý với việc hạn chế thời gian.
ATaco

1
Điều này nghe có vẻ thực sự quen thuộc, đặc biệt là sau khi nhìn vào cốt truyện. Hóa ra tôi đã xem xét một chuỗi liên quan rất chặt chẽ vào đầu năm nay, nhưng tôi đã đếm số lượng số nhị phân riêng biệt, không phải chuỗi nhị phân, bạn nhận được khi lấy các số sau (vì vậy tôi đã giảm giá các số 0 đứng đầu). Tôi thậm chí đã đóng hộp nó, nhưng do sự tương đương trong bài Math.SE, nó sẽ là một bản sao của một số thách thức Stern-Brocot. Cốt truyện của trình tự của bạn đẹp hơn một chút (nghĩa là hỗn loạn hơn). :)
Martin Ender

5
@ATaco Hạn chế thời gian có lý do chính đáng. Có một thuật toán hiệu quả, và nó rất thú vị nhưng cũng có thể chơi được. Nếu tôi không bị hạn chế về thời gian, tôi cảm thấy rằng gần như mọi câu trả lời chỉ đơn giản là vũ phu tất cả các phần sau có thể, rất nhanh sẽ không còn hiệu quả nữa. Theo một nghĩa nào đó, chúng không phải là câu trả lời.
orlp

Câu trả lời:


10

Python 3 , 95 byte 83 byte

[-12 byte nhờ 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

Hãy thử trực tuyến!

Một lưu ý về thuật toán. Thuật toán tính toán gia số trong các chuỗi con duy nhất được cho bởi bit tại một vị trí nhất định t. Gia số của bit đầu tiên luôn là 1. Thuật toán sau đó chạy qua chuỗi bit s (t) và thêm gia số v [s (t)]. Ở mỗi bước, gia số cho phần bù của s (t), v [1 - s (t)] được cập nhật thành v [1] + v [0]. Số cuối cùng là tổng của tất cả các số gia.

Nó sẽ chạy trong O (log2 (n)), trong đó n là số đầu vào.



8

JavaScript (ES6), 53 51 byte

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

Các trường hợp thử nghiệm

Định dạng và nhận xét

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

Phiên bản không đệ quy, 63 byte

Đã lưu 3 byte nhờ @ThePirateBay

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

Các trường hợp thử nghiệm


Tôi nghĩ bạn có thể lưu 3 byte bằng cách gán hàm bên trong (đối số đầu tiên của map) cho biến cờ lthay vì một mảng trống.

@ThePirateBay Đẹp một cái. Cảm ơn!
Arnauld


6

Thạch , 10 byte

B3;BSṛ¦/’S

Điều này sử dụng cải tiến của @ xnor trên thuật toán của @ NofP .

Hãy thử trực tuyến!

Lý lịch

Đặt (a 1 , ..., a n ) là một chuỗi nhị phân hữu hạn. Đối với mỗi số nguyên không âm k ≤ n , xác định o k là số lượng các chuỗi con duy nhất của (a 1 , ..., a k ) trống hoặc kết thúc bằng 1 , z k là số lượng các chuỗi con duy nhất là hoặc trống hoặc kết thúc bằng 0 .

Rõ ràng, o 0 = z 0 = 1 , vì chuỗi con duy nhất của chuỗi trống là chuỗi trống.

Đối với mỗi chỉ số k , tổng số chuỗi con của (a 1 , ..., a k )o k + z k - 1 (trừ 1 tài khoản cho thực tế là cả o kz k đều tính chuỗi trống). Do đó, tổng số các chuỗi không trốngo k + z k - 2 . Thử thách yêu cầu tính o n + z n - 2 .

Bất cứ khi nào k> 0 , chúng ta có thể tính toán đệ quy o kz k . Có hai trường hợp:

  • a k = 1

    z k = z k - 1 , vì (a 1 , ..., a k - 1 )(a 1 , ..., a k - 1 , 1) có cùng một chuỗi kết thúc bằng 0 .

    Đối với mỗi o k - 1 subsequences không trống của (một 1 , ..., a k ) mà kết thúc trong 1 , chúng ta có thể loại bỏ các dấu 1 để có được một trong những o k-1 + z k-1 - 1 phần sau (a 1 , ..., a k-1 ) . Ngược lại, việc thêm 1 vào mỗi chuỗi sau o k-1 + z k-1 - 1 dẫn đến một trong các chuỗi o k - 1 trước đó. Do đó, o k - 1 = ok - 1 + z k - 1 - 1 o k = o k - 1 + z k - 1 .

  • a k = 0

    Tương tự như trường hợp trước, chúng ta thu được các công thức đệ quy o k = o k - 1z k = z k - 1 + o k - 1 .

Làm thế nào nó hoạt động

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.

hey dennis, bạn có phiền khi thêm một lời giải thích ngắn về lý do tại sao thuật toán hoạt động không?
Giô-na

Tôi đã thêm một lời giải thích.
Dennis

4

05AB1E , 12 byte

0¸sbvDO>yǝ}O

Hãy thử trực tuyến! Giải thích: Như được chỉ ra bởi các câu trả lời khác, số lượng chuỗi a..y0con cho chuỗi nhị phân kết thúc bằng 1 giống như số của chuỗi nhị phân a..y, trong khi số kết thúc bằng a 0là tổng số chuỗi con cho nhị phân chuỗi a..y(mà mỗi chuỗi có một 0hậu tố) cộng với một cho 0chính nó. Không giống như các câu trả lời khác, tôi không bao gồm phần tiếp theo trống vì điều này sẽ lưu một byte xây dựng trạng thái ban đầu.

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

1

Java 8, 97 byte

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;}

Câu trả lời Python 2 của @xnor , lần lượt là sự cải tiến của câu trả lời Python 3 của @NofP .

Hãy thử nó ở đây.


Có lẽ đó là một điều tốt khi thẻ có mặt, bởi vì ban đầu tôi có những điều sau đây để đánh bại tất cả các phần sau:

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;}

Hãy thử nó ở đây.

Điều này cũng làm việc, nhưng mất quá nhiều thời gian cho ba trường hợp thử nghiệm cuối cùng. Chưa kể nó dài hơn ( 208 204 byte ).


1

Mã máy 6502 (C64), 321 byte

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

Bản demo trực tuyến

Bản demo trực tuyến với kiểm tra lỗi (346 byte)

Cách sử dụng: sys49152,[n] vd sys49152,911188917558917.

Giới hạn thời gian và các trường hợp thử nghiệm yêu cầu các giải pháp để tính toán bằng số 64 bit, vì vậy thời gian để chứng minh C64 đủ điều kiện là " máy hiện đại ";)

Tất nhiên, điều này cần khá nhiều mã, HĐH không cung cấp bất cứ thứ gì cho số nguyên rộng hơn 16 bit. Phần khập khiễng ở đây: đó là một triển khai khác (được sửa đổi một chút) về sự tôn trọng thuật toán của NofP . biến thể cải tiến của xnor . Cảm ơn vì ý tưởng;)


Giải trình

Dưới đây là danh sách tháo gỡ nhận xét của phần có liên quan thực hiện thuật toán:

.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       

Phần còn lại là đầu vào / đầu ra và chuyển đổi giữa chuỗi và số nguyên không dấu 64 bit (little endian) bằng cách sử dụng một số thuật toán ghép đôi. Trong trường hợp bạn quan tâm, đây là toàn bộ nguồn lắp ráp cho phiên bản có kiểm tra lỗi - phiên bản "được đánh gôn" nằm trong nhánh "golf".

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.