二元展开式的不同非空子序列数


19

子序列是您可以通过删除任意数量的字符从另一个序列获得的任何序列。的不同的非空的子序列100010010100。的不同的非空的子序列101001000110110101001011101010

写一个程序或函数给定一个正整数Ñ返回的二进制展开的不同的非空的子序列的数目Ñ

例子:因为4100二进制,所以我们看到上面有五个不同的非空子序列,所以f(4) = 5。从n = 1开始,序列开始:

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

但是,您的程序必须在任何现代机器上都能在n <2 50秒内工作。一些大的例子:

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

4
我不同意时间限制。
ATaco

1
这听起来确实很熟悉,尤其是在查看情节之后。事实证明,我在今年早些时候研究了一个非常紧密相关的序列,但是我计算了取子序列时得到的不同二进制数(而不是二进制字符串)的数量(因此我减去了前导零)。我什至对它进行了沙盒处理,但是由于在Math.SE帖子中具有同等学力,所以它可能是对Stern-Brocot挑战的欺骗。不过,您的序列图更好一些(即更混乱)。:)
Martin Ender

5
@ATaco时间限制是有充分理由的。有一种有效的算法,既有趣又很好打。如果我没有时间限制,我会觉得几乎每个答案都只会对所有可能的子序列进行暴力破解,而这些子序列很快将不再起作用。从某种意义上说,它们不是答案。
Orlp

Answers:


10

Python 3中95字节 83个字节

[-12字节,感谢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

在线尝试!

关于算法的注释。该算法计算在给定位置t的位给出的唯一子序列的增量。第一位的增量始终为1。然后,算法在位s(t)的序列上运行并添加增量v [s(t)]。在每个步骤中,将s(t)的补码v [1-s(t)]的增量更新为v [1] + v [0]。最终数字是所有增量的总和。

它应该在O(log2(n))中运行,其中n是输入数字。



8

JavaScript(ES6),53 51字节

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

测试用例

格式化和评论

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

非递归版本,63字节

感谢@ThePirateBay,节省了3个字节

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

测试用例


我认为您可以通过将内部函数(的第一个参数map)分配给flag变量l而不是空数组来节省3个字节。

@ThePirateBay好一个。谢谢!
Arnauld


6

果冻,10字节

B3;BSṛ¦/’S

这使用@xnor@NofP算法的改进

在线尝试!

背景

(a 1,...,a n为有限二进制序列。对于每个非负整数k≤n,将o k定义为(a 1,...,a k为空或以1结尾的唯一子序列的数量,z k为以下子元素的唯一子序列的数量:为空或以0结尾。

显然,o 0 = z 0 = 1,因为空序列的唯一子序列是空序列。

对于每个索引k(a 1,...,a k的子序列总数为o k + z k -1(减去1表示o kz k都计算空序列的事实)。因此,非空子序列的总数为o k + z k -2。挑战要求计算o n + z n -2

只要k> 0,我们就可以递归计算o kz k。有两种情况:

  • 一个ķ = 1

    z k = z k-1,因为(a 1,...,a k-1(a 1,...,a k- 1,1)具有相同的以0结尾的子序列。

    对于每个的Ó ķ - 1的非空的子序列(一个1,...,A ķ在该端1,我们可以删除后1以获得之一ø K-1 + Z k-1个 - 1个子序列(a 1,...,k-1。相反,在后面的o k-1 + z k- 1-1序列中的每一个后面加上一个1,就可以得出o k -1前面的序列之一。因此,o k -1 = oK-1 + Z k-1个 - 1 ö ķ = O K-1 + Z k-1个

  • 一个ķ = 0

    与前一种情况类似,我们获得了递归公式o k = o k-1z k = z k-1 + o k-1

怎么运行的

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.

嘿,丹尼斯,您介意为该算法为什么添加简短说明吗?
约拿(Jonah)

我添加了一个解释。
丹尼斯

4

05AB1E,12个字节

0¸sbvDO>yǝ}O

在线尝试!说明:如其他答案所指出的a..y0,以1结尾的二进制字符串的子序列数与该二进制字符串的数目相同a..y,而以a结尾0的数字是该二进制数的子序列总数字符串a..y(每个都有一个0后缀)加上一个0自己。与其他答案不同,我不包括空子序列,因为这节省了构造初始状态的字节。

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字节

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

@xnor的Python 2答案的端口,这反过来又是@NofP的Python 3答案的改进。

在这里尝试。


限时标签可能是一件好事,因为我最初具有以下条件来蛮力所有子序列:

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

在这里尝试。

这也有效,但对于最后三个测试用例花费的时间太长。更不用说它要更长(208 204字节)。


1

6502机器代码(C64),321字节

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

在线演示

带有错误检查的在线演示(346字节)

用法: sys49152,[n]例如sys49152,911188917558917

时间限制和测试用例要求解决方案以64位数字进行计算,因此有时间证明C64符合“ 现代机器 ”的资格;)

当然,这需要大量的代码,对于宽于16bit的整数,操作系统不提供任何功能。这里最la脚的部分是:这是NofP算法的另一种实现(略作修改)。xnor的改进版本。谢谢你的主意;)


说明

这是执行算法的相关部分的注释反汇编清单:

.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       

其余的是输入/输出,并使用一些双浮点算法在字符串和64位无符号整数(little-endian)之间进行转换。如果您有兴趣,这里是带错误检查版本的整个程序集源-“ golfed”版本位于“ golf”分支中。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.