整数的数字硬度


26

要找到整数的数字硬度,请使用二进制表示形式,并计算可以删除前导尾随的次数,1直到它以a开头或结尾为止0。去除的总位数是其数字硬度。

这是一个冗长的解释-因此,让我们用一个可行的例子来分解它。

在此示例中,我们将使用数字3167。在二进制文件中,这是:

110001011111

(请注意,在转换为二进制文件的过程中,应确保去除前导零)

它不是以开头或结尾0,因此我们删除了1对位:

1  1000101111  1

还有一个:

11  00010111  11

但是现在开始时有一个0,所以我们不能再删除1对了。总共4个比特我们除去,所以4是数字硬度3167。

但是,对于可以写为2 n -1表示正n的数字(即仅包含1二进制表示形式),永远不会达到0,因此可以删除所有位。这意味着硬度只是整数的位长。


挑战

您的任务是编写一个程序或函数,给定一个非负整数n >= 0,该程序或函数将确定其数字硬度。

您可以提交执行I / O的完整程序,或提交返回结果的功能。您的提交应适用n于您的语言的标准整数范围内的值。


测试用例

如果其中任何一个不正确,或者您想建议添加任何极端情况,请通知我。

0     -> 0
1     -> 1
8     -> 0
23    -> 2
31    -> 5
103   -> 4
127   -> 7
1877  -> 2
2015  -> 10

这是我用来生成这些测试用例的非解决方法(不保证没有错误):

def hardness(num) -> int:
    binary = bin(num)[2:]

    if binary.count('0') == 0:
        return num.bit_length()

    revbin = binary[::-1]

    return min(revbin.find('0'), binary.find('0')) * 2

1
1没有0任何内容时如何返回1 ?我的意思是,您不可能从字符串中删除足够多的1来使其开始或结束于0
busukxuan

2
@busukxuan阅读“挑战”标题之前的段落:对于可以写为2 ^ n-1的数字(即二进制表示形式仅包含1),永远不会达到0,因此可以删除所有位。这意味着硬度只是整数的位长。
FlipTack

2
@busukxuan可以认为是在到达零之前,每边填充了一个。
FlipTack

2
对于显然不喜欢边缘情况的下注者:硬度是填充有实心(1)钻头的数量-如果整个物体都是实心的,那么它肯定具有100%的硬度,即整个钻头的长度?
FlipTack

1
@FlipTack我不想影响太大,这是您的挑战。我最初将“硬度”理解为可以去除的一外部的最大数量,每侧一对。但是您可能是对的,如果最后只剩下一个,也许应该算在内
Luis Mendo

Answers:


6

果冻11 10字节

BµQL××Ṛa\S

在线尝试!

怎么运行的

BµQL××Ṛa\S  Main link. Argument: n

B           Binary; convert n to base 2.
 µ          Begin a new, monadic chain. Argument: A (array of binary digits)
  Q         Unique; deduplicate the digits.
   L        Length; count the unique digits.
    ×       Multiply each digit by the result.
     ×Ṛ     Multiply the results by reversed A.
       a\   Cumulative reduce by logical AND.
            This zeroes out all elements after the first zero.
         S  Compute the sum of the result.

8

Python76 69 68 63 62 60 57字节

f=lambda n,k=0:n>>k&(n&n>>k>n>>k+1)and(n&n+1>0)-~f(n,k+1)

在线尝试!

怎么运行的

这是一个递归解决方案,它采用输入n并保持k的增加–从0开始–同时LSB k(n)(位于右侧索引k的位)和MSB k(n)(位于左侧索引k的位)被设置。完成后,如果所有n位都置位,则返回k,否则返回2k

让我们通过重写拉姆达开始˚F作为命名功能˚F,有一个辅助变量牛逼

def F(n, k = 0):
    t = n >> k
    return t & (n & t > t >> 1) and (n & (n + 1) > 0) + 1 + F(n, k + 1)

在每次调用F时,我们首先将n总共向右移位k个单位,并将结果存储在t中。这样,LSB 0(T)= LSB ķ(n)的,所以为奇数当且仅当LSB ķ(n)的被设置。

确定是否设置了MSB k(n)会比较麻烦。这就是成就n & t > t >> 1。为了说明它是如何工作,让我们考虑一个整数N =1αβγδεζη 2比特长度的8和分析函数调用F(N,3) ,即,K = 3

我们试图确定是否MSB 3(N)=γ是通过检查比较的真值设为(N&T> T >> 1)=(1αβγδεζη 2&1αβγδ 2 >1αβγ 2。让我们检查涉及的整数。

MSB-index  012k4567

n          1αβγδεζη
t             1αβγδ

t >> 1         1αβγ

当且仅当n&t> t >> 1时,我们认为γ= 1

  • 如果γ= 1,则n&t的位长为5,t >> 1的位长为4,因此n&t> t >> 1

    这证明γ= 1意味着n&t> t >> 1

  • 如果n&t> t >> 1,则有两个选择:γ= 1γ= 0。在第一种情况下,没有什么可证明的。

    在第二种情况下,我们有αβγδ 2 ≥N&T> T >> 1 =1αβγ 2

    由于αβγδ 2 >1αβγ 2,就必须有MSB 0(αβγδ 2)≥MSB 0(1αβγ 2,也就是说,α= 1

    这样,1βγδ 2 >11βγ 2,所以我们必须有MSB 1(1βγδ 2)≥MSB 1(11βγ 2,也就是说,β= 1

    反过来,这意味着11γδ 2 >111γ 2。记住的是γ= 0在第二种情况下,我们得到的不等式110δ 2 > 1110 2,这是因为假MSB 2(110δ 2)= 0 <1 = MSB 2(1110 2

    因此,只有第一种情况是可能的,并且n&t> t >> 1意味着γ= 1

总结一下,如果同时设置了LSB k(n)MSB k(n),则t将为奇数,并且n&t> t >> 1将为True,因此t&(n&t> t >> 1)将为产量1。但是,如果未设置LSB k(n)MSB k(n)(或如果两者都设置),则t将为偶数,或者n&t> t >> 1将为False,因此t&(n&t> t> > 1)将产生0

使用单个参数调用F会初始化k = 0。虽然我们前面讨论的条件成立,之后的代码and被执行,其中(除其他事项外)递归调用˚F与递增ķ

一旦未设置LSB k(n)MSB k(n),该条件将失败并且F(n,k)返回0。前面的k个函数调用中的每个函数都将(n&(n + 1)> 0)+ 1加到F(n,k)= 0,所以F(n)返回((n&(n + 1)> 0)+ 1)k

现在,如果n的所有位都相等(即,如果n0或它的所有位都已设置),则n +1将不具有与n相同的任何位,因此n&(n + 1)= 0F(n)返回k。但是,如果n同时具有置位和未设置位,则n&(n + 1)> 0,并且F(n)返回2k


2
最近,Python中的递归解决方案似乎得分很高。
mbomb007'1

至少与迭代解决方案相比,它们始终具有。input()whileprint已经是17个字节...
丹尼斯

是的,但是我发现它们很难编写。
mbomb007'1

1
很公平。但是,相同想法的直接迭代实现只会增加5个字节。tio.run/nexus/…可以通过一些技巧来节省2个字节。tio.run/nexus/python2#JY1BDsIgFAXX7SnepgUUI1BNm1K4jKVJQ/…–
Dennis,

6

MATL13 12字节

Btv`6L&)}x@q

在线尝试!验证所有测试用例

说明

该代码重复每个二进制数字,并计算可以删除两个外部数字的次数。

B        % Input number (implicit). Horizontal vector of binary digits
tv       % Duplicate and concatenate vertically
`        % Do...while
  6L&)   %   Flatten the array if needed (in column-major order), and split it
         %   into two subarrays: one with the inner entries, and another
         %   with the two outer entries. The latter will be used for deciding
         %   if the loop continues or is exited
}        % Finally (execute before exiting the loop)
  x      %   Delete last subarray of inner entries
  @q     %   Push last iteration index minus 1
         % End (implicit). The next iterarion is executed if the array at the
         % top of the stack is non-empty and only contains nonzero values. 
         % Otherwise the loop is exited, executing the "finally" block first
         % Display (implicit)

6

Python,82个字节

我觉得它仍然可以打高尔夫球,但是我花了一段时间尝试其他方法,这是最短的。

def f(n):b=bin(n)[2:];x=min(b.find('0'),b[::-1].find('0'));print(x<0)*len(b)or x*2

在线尝试

尽管其工作方式类似于OP的Python程序,但我在发布问题之前在不包含此类程序的沙箱中查看了问题之后创建了该程序。


6

Python 2,66字节

s=bin(input())[2:].split('0')
print len(min(s[-1],s[0]))<<1%len(s)

将输入的二进制表示形式拆分为1的块。在第一个和最后一个块中较小的那个中计算1的数目,然后将其加倍,除非有单个块将其重复计数。


聪明,但仍然容易理解。我喜欢!
mbomb007'1

5
@ mbomb007将其作为了解丹尼斯(Dennis)的热身:)
xnor

3

PowerShell109106字节

$a=[convert]::ToString($args[0],2)-split0;(((($b=$a[0].length),$a[-1].length|sort)[0]*2),$b)[$a.count-eq1]

在线尝试!

需要输入$args[0],使用.NET调用converttoString与基地2(即,使其二进制),然后-splits表示字符串的0S,商店到这一点$a。重要说明:.NET调用不会返回前导零,因此第一位始终是a 1

因此,存在两种可能性-二进制字符串全为1,或者至少有一个零。我们用伪三元索引为的那些进行区分$a.count-eq1。如果二进制文件至少有一个零(左情况),我们将[0]采用1s 的第一个字符串和最后一个[-1]字符串(由by找到|sort,然后为[0])的最小长度。这些中的较短者是我们可以删除的对,因此我们将其乘以2。请注意,如果原始的二进制字符串以a结尾0,例如用于输入8,则a [-1].length也将是0(因为它是一个空字符串),当乘以时2仍然是0

否则,将二进制字符串全为1(我们将$b其设置为第一个[0]字符串的长度,在这种情况下为二进制字符串的整个长度)。

无论哪种情况,结果都留在管道上,输出是隐式的。


3

JavaScript(ES6),57个字节

f=
n=>n.toString(2).replace(/^(1*)(.*(\1))?$/,'$1$3').length
<input oninput=o.value=1/this.value?f(+this.value):''><input id=o readonly>

取二进制文件并尝试匹配全部1s或失败的相等数量的前导和尾随1s


2

视网膜,48字节

.+
$*
+`(1+)\1
$1o
o1
1
m(+`^1(.*)1$
xx¶$1
x|^1$

在线尝试

说明:

.+              # Convert to unary
$*
+`(1+)\1        # Convert to binary (but with `o` instead of `0` -- it's shorter)
$1o
o1
1
m(+`^1(.*)1$    # Replace pairs of surrounding ones with `xx`
xx¶$1
x|^1$,          # Count x's, including the possibility of a single remaining `1`

2

C#,133个字节

返回硬度的功能。从参数中获取整数。

int h(int b){var n=Convert.ToString(b,2);for(b=0;;){if(n[0]+n[n.Length-1]==98)n=n.Substring(1,n.Length-2);else break;b+=2;}return b;}

好吧,今天我'1' + '1' = 98在C#中找到了。


1
那是因为'1'是ASCII字符49和49 + 49 =
98。– FlipTack

我实际上花了10分钟弄清楚为什么我1 + 1 = 2没工作。@FlipTack
devRicher

2

C,89 88 85字节

由于@FlipTack指出了无用的声明,因此节省了两个字节。

f()用数字调用进行测试,输出从函数返回。

t,h;f(l){for(t=l;t&&~t&1<<30;t*=2);for(h=0;t&1<<30&&l&1;t*=2,l/=2)++h;return h<<!!l;}

在ideone上尝试一下


2

JavaScript(ES6),59 58字节

f=(n,m=1<<30)=>m>n?f(n,m/2):m>1?n&m&&n&1&&2+f(n/2,m/4):n&1

测试用例



2

C,137 132 122 119 117 114 98 94 92 87 85字节

是时候开始打高尔夫球了B-)

i,j;f(n){for(i=1<<30;i&~n;i/=2);for(j=0;n&i;n/=2,i/=4)j+=~n&1?i=0:2;return j-=n<1*j;}

这是证明

main()
{
  printf("%d %d\n", 0, f(0));
  printf("%d %d\n", 1, f(1));
  printf("%d %d\n", 8, f(8));
  printf("%d %d\n", 23, f(23));
  printf("%d %d\n", 31, f(31));
  printf("%d %d\n", 103, f(103));
  printf("%d %d\n", 127, f(127));
  printf("%d %d\n", 1877, f(1877));
  printf("%d %d\n", 2015, f(2015));
  printf("%d %d\n", 3167, f(3167));
} 

和输出;

0 0
1 1
8 0
23 2
31 5
103 4
127 7
1877 2
2015 10
3167 4 


1

Mathematica,63个 56字节

(2-Min[l=#~IntegerDigits~2])Min[Tr/@Split[l][[{1,-1}]]]&

说明

l=#~IntegerDigits~2

生成输入的以2为基的表示形式,并用包裹List。存放在l

(2-Min[...])

如果的最小元素l为1,则输出1。如果不是,则输出2。

Split[l]

拆分l成运行。

... [[{1,-1}]]

取第一个和最后一个元素。

Tr/@ ...

两者合计。

Min[ ... ]

找到两者之间较小的一个。

(将第一个结果(1或2)与此结果相乘)。


1

八度,56 54字节

 @(n)cummin(d=dec2bin(n)-48)*cummin(flip(d))'*2^!all(d)

在线尝试!

说明:

d=dec2bin(n)-48

的二进制表示 n

cumd= cummin(d);
cumfd = cummin(flip(d));

取的累积最小值d 和累积的最小值d

res = cumd * cumfd ';

做矩阵乘法

out = res*2^!all(d)

如果所有数字均为1,则乘以2;


@FlipTack谢谢,链接已更新!
rahnema17年

1

Pyth,18个字节

?*FJjQ2lJyhSxR0_BJ

接受整数输入并打印结果的程序。

测试套件(格式化的第一行)

怎么运行的

?*FJjQ2lJyhSxR0_BJ  Program. Input: Q
?                   If
  F                 reducing
    jQ2             the binary representation of Q as a list
   J                (store in J)
 *                  by multiplication is truthy:
       lJ            Yield len(J)
                    Else:
          hS         Yield the minimum
            xR0      of the first index of zero
               _BJ   in J and its reverse
         y           * 2
                    Implicitly print

1

APL,26个字节

+/∘(∧\≢↑(∊⊢(,∧∧)¨⌽))2⊥⍣¯1⊢

测试用例:

      ( +/∘(∧\≢↑(∊⊢(,∧∧)¨⌽))2⊥⍣¯1⊢ ) ¨ 0 1 8 23 31 103 127 1877 2015    
0 1 0 2 5 4 7 2 10

说明:

+ /∘(∧\≢↑(∊⊢(,∧∧)¨⌽))2⊥⍣¯1⊢

                         ⊢输入
                    2⊥⍣¯1转换为二进制表示
   ()
        (⊢¨)每个位及其另一位的匹配位
            (∧)取两个位的逻辑和
             ,列出两个位,
              ∧然后取列表中的和以及
         ∊展平结果数组
      ≢↑仅取前N位,其中N是
                                原始位列表的长度
    ∧\具有运行逻辑,并且(仅保留
                                起始的)
+ /∘总和

1

J,22个字节

(#<.2*(<.&(#.~)|.))@#:

这是基于从这项挑战中学到的巧妙技巧。

在线尝试!

说明

(#<.2*(<.&(#.~)|.))@#:  Input: integer n
                    #:  Binary digits of n
(                 )@    Operate on those digits D
               |.         Reverse D
       <.                 Take the minimum of
         &(#.~)           the "trailing truths" of D and reverse(D)
    2*                    Multiply by 2
 #                        The length of D
  <.                      Minimum of length and the previous result

1

PHP,83 74字节

Jörg保存了3 + 6字节

<?=(~$s=decbin($argn))[$a=strspn($s,1)]?min($a,strspn(strrev($s),1))*2:$a;

接收来自STDIN的输入;与运行-nR

分解

<?=                     # print ...
(~
    $s=decbin($argn)        # $s = binary representation of input
)[
    $a=strspn($s,1)         # $a = number of leading `1`s
]                           # if $s has more than $a digits,
?   min($a,                     # 2. minimum of $a and
        strspn(strrev($s),1)    # 1. number of trailing `1`s
    )*2                         # 3. *2
:   $a                      # else $a (==strlen)

1
<?=~($s=decbin($argn))[$a=strspn($s,1)]?2*min($a,strspn(strrev($s),1)):$a;
约尔格Hülsermann

0

JavaScript(ES6),83个字节

f=x=>(y=x.toString(2),y.match(/^1*$/)?y:([s,e]=y.match(/^1*|1*$/g),s<e?s:e)).length

取消高尔夫:

function f(n) {
    var binStr = n.toString(2);
    if(binStr.match(/^1*$/)) {
        // If binary representation is all 1s, return length of binary
        return binStr.length;
    } else {
        // Grab the starting and ending 1s in the binary representation
        var [start1s, end1s] = binStr.match(/^1*|1*$/g);
        var startHardness = start1s.length;
        var endHardness = end1s.length;
        return Math.min(startHardness, endHardness);
    }
}

0

Mathematica,62个字节

(h=0;#~IntegerDigits~2//.{{1,m___,1}:>(h+=2;{m}),{1}:>h++};h)&

纯函数where #代表第一个参数。

(h=0;...;h)&设置h=0,做一堆东西...,然后返回h(硬度)。让我们看一堆东西:

#~IntegerDigits~2                                     Binary representation of the input
                 //.                                  Apply the following list of rules repeatedly until there is no change
                    {                                 Start of the list of rules
                     {1,m___,1}                       If you see a list starting and ending with 1 with the sequence m (possibly empty) in between
                               :>(h+=2;{m}),            replace it with just {m} after incrementing h twice.
                                            {1}       If you see the singleton list {1}
                                               :>h++    replace it with h, then increment h.
                                                    } End of the list of rules

感谢Greg Martin向我介绍了这个技巧


0

Haskell94 92字节

b 0=[]
b n=mod n 2:b(div n 2)
h n|(c,_:_)<-span(>0)$zipWith(*)n$reverse n=c++c|1<3=n
sum.h.b

在线尝试!用法:

Prelude> sum.h.b $ 3167
4

说明:
b将数字转换为二进制,并返回零和一的列表,其最低有效位在前。在中h,此列表被反转,并按元素与原始列表相乘,然后span(>0)在初始1s 之后拆分:

       b 3167 = [1,1,1,1,1,0,1,0,0,0,1,1] = n
    reverse n = [1,1,0,0,0,1,0,1,1,1,1,1] = m
zipWith(*)n m = [1,1,0,0,0,0,0,0,0,0,1,1] = z
   span(>0) z = ([1,1],[0,0,0,0,0,0,0,0,1,1])

生成的元组与模式匹配,(c,_:_)其中_:_匹配任何非空列表,因此c = [1,1]。因为字节是从前面和后面除去的,c++c = [1,1,1,1]所以返回并最终求和以产生数字硬度

如果元组的第二个列表为空,则二进制表示形式仅包含一个,并且个数为数字硬度。在模式匹配失败的h情况下,just n会再次返回。


0

Perl,61个字节

sub f{$_=sprintf('%b',pop);length(/0/?/^(1+).*\1$/&&$1x2:$_)}

正则表达式的核心是答案的/^(1+).*\1$/2倍$1。剩下的代码是开销,并且处理全1的特殊情况。


您可以省略sprintf参数周围的括号。同样,使用-pflag将允许您编写一个比您的函数短的完整程序,因为您可以省略它sub f{...}(相反,您必须以结尾,$_=...但这仍然是4字节的改进)。最后,length(...)您可以代替您/0/&&s/^(1+).*\1$/$1$1/;$_=y///c。这应该使您达到51个字节。
Dada's


0

CJam,14个字节

ri2b_0#\W%0#e<

说明:

ri e# Read integer:      | 3167
2b e# Convert to binary: | [1 1 0 0 0 1 0 1 1 1 1 1]
_  e# Duplicate:         | [1 1 0 0 0 1 0 1 1 1 1 1] [1 1 0 0 0 1 0 1 1 1 1 1]
0# e# Index of first 0:  | [1 1 0 0 0 1 0 1 1 1 1 1] 2
\  e# Swap:              | 2 [1 1 0 0 0 1 0 1 1 1 1 1]
W% e# Reverse:           | 2 [1 1 1 1 1 0 1 0 0 0 1 1]
0# e# Index of first 0:  | 2 5
e< e# Minimum:           | 2
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.