我适合多少位


52

对于任何正的32位整数(1 ≤ n ≤ 0xFFFFFFFF)输出,代表该整数所需的位数。

测试用例

| n    | n in binary | bits needed |
|----------------------------------|
| 1    | 1           | 1           |
| 2    | 10          | 2           |
| 3    | 11          | 2           |
| 4    | 100         | 3           |
| 7    | 111         | 3           |
| 8    | 1000        | 4           |
| 15   | 1111        | 4           |
| 16   | 10000       | 5           |
| 128  | 10000000    | 8           |
| 341  | 101010101   | 9           |

4294967295 => 11111111111111111111111111111111 => 32

所以f(16)会打印或返回5

这是。以字节为单位的最短代码获胜


2
这是以2为底的对数的上限。
orlp

23
@orlp实际上是floor(log2(num))+1
Kritixi Lithos 16/12/27

2
@KritixiLithos对。
orlp

3
没关系,只要意识到区别是重要的,那num是两个的幂。
布赖恩·J

11
这是一个琐碎的挑战,有很多琐碎的解决方案。但是,也有一些非平凡的解决方案。致选民:在支持内置功能之前请阅读此元文章第一句话。(摘自此评论
Kritixi Lithos

Answers:



35

JavaScript(ES6),18个字节

f=n=>n&&1+f(n>>>1)
<input type=number min=0 step=1 value=8 oninput="O.value=f(this.value)">
<input id=O value=4 disabled>


这是这里为数不多的简单解决方案之一。不错的战术!
Kritixi Lithos

1
应该n>>>1支持n > 0x7FFFFFFF吗?
Arnauld

@Arnauld Hmm,不知道>>失败n那么高。谢谢。
ETHproductions 2016年

不错,我的第一次尝试是f=(a,b=1)=>a>1?f(a>>1,++b):b
Bassdrop Cumberwubwubwub

28

x86汇编,4字节

假设常量为EBX

bsr eax,ebx
inc eax

EAX包含常数所必需的位数。

字节数: ☼¢├@

十六进制: ['0xf', '0xbd', '0xc3', '0x40']


2
您能否包含实际的8字节已编译x86汇编代码的十六进制转储?
Loovjo

是的 谢谢,因为我意识到自己犯了一个错误。我添加了一个“ inc eax”以符合规则。我丢了一个字节。:(
z0rberg's

噢,您将我的帖子更改为正确的格式。感谢您纠正它!
z0rberg's

2
顺便说一下,Assembly提交可以假定输入已经存储在特定的寄存器中,因此我认为您可以这样节省一些字节。
Loovjo

1
按照惯例将程序集提交计数为已编译机器代码而不是汇编语言源代码的字节数吗?
smls

18

Python,14个字节

int.bit_length

在线尝试!


它也适用于Python 2。
vaultah '16

1
确实如此。忘了Python 2的int是64位宽,而不是32位。
丹尼斯

Python 3 bit_lengthbit_length()
dfernan

2
@dfernan这不是函数调用;这是一个功能。如果ñ是一个INTint.bit_length(n)n.bit_length()做完全一样的。
丹尼斯

2
@dfernan int.bit_length(n)是一个函数调用,因此假设输入的代码段存储在变量中。这是以我们的规则允许的,所以追加(n)将使这个答案无效。但是,int.bit_length对函数求值,可以将其保存在变量中以备后用。默认情况下允许。
丹尼斯

15

迷宫13 12字节

 ?
_:
2/#(!@

在线尝试!

说明

程序只是简单地将输入除以2,直到为零为止。通过在每个步骤中复制值来跟踪步骤数。一旦减少到零,我们将打印堆栈深度(减1)。

程序从?读取输入的起始位置。然后,主循环是下面的2x2块,逆时针旋转:

:   Duplicate current value.
_2  Push 2.
/   Divide.

完整迭代后,一旦该值为零,则执行末尾的线性位:

#   Push stack depth.
(   Decrement.
!   Print.
@   Terminate.

5
该解决方案是完整的-它接受输入并提供答案,并且不为此目的使用任何现有功能-它手动计算答案。对我而言,这比大多数其他答案更符合游戏精神。
约翰,

15

C,31个字节

f(long n){return n?1+f(n/2):0;}

...然后我想到了递归。从晦涩到明显,长度的四分之一下降。

在Coliru上实时观看


C,43个字节

c;
#define f(v)({for(c=0;v>=1l<<c;++c);c;})

f使用无符号值(例如f(42u))进行调用将“返回”其位长。甚至适用于0u

取消说明:(省略反斜杠)

c;
#define f(v)
    ({ // GCC statement-expression

        // Increment c until (1 << c) >= v, i.e
        // that's enough bits to contain v.
        // Note the `l` to force long
        // arithmetic that won't overflow.
        for(c = 0; v >= 1l << c; ++c)
            ; // Empty for body

        c; // Return value
    })

在Coliru上实时观看


OP保证n> = 1,所以n?...:0没有必要。
疯狂物理学家,2013年

1
@MadPhysicist好吧,我确实必须在某处停止递归,对吧;)
Quentin

OIC。没有仔细阅读,现在感觉像个笨蛋。两种方法的回答都很简洁。
疯狂物理学家

@MadPhysicist不用担心,非常感谢:)
Quentin

对于使用gcc语句表达式的非递归解决方案,我认为您可能也倾向于使用该#define f(n) ({64-__builtin_clzl(n);})方法。
Moreaki '17

14

Mathematica,9个字节

BitLength

或者:

Floor@Log2@#+1&
#~IntegerLength~2&



12

视网膜56 37字节

该解决方案适用于所有必需的输入值。

Retina在此挑战中面临的最大问题是其字符串的最大长度为2 ^ 30个字符,因此处理数字(一元表示)的通常方法不适用于大于2 ^ 30的值。

为了解决此问题,我采用了另一种方法,即保留一种数字的十进制表示形式,但是其中的每个数字都以一进制表示(我将其表示为digitunary)。例如,数字341将以数字形式书写111#1111#1#。有了这种表示形式,我们现在可以使用不超过2^30/10数位(约一亿位数)的数字。对于任意算术而言,它不如标准一元代码实用,但是通过一些努力,我们可以执行任何类型的运算。

注意:理论上,数字二进制可以使用任何其他基数(例如,二进制110将以1#1##2 进制数字二进制为基数),但是由于Retina具有内置函数可以在十进制和一进制之间进行转换,并且没有直接处理其他基数的方法,因此十进制可能是最易于管理的基数。

我使用的算法是将连续的整数除以2,直到达到零为止,除法的次数就是表示该数字所需的位数。

那么,我们如何将数字除以二呢?这是做到这一点的Retina片段:

(1*)(1?)\1#        We divide one digit, the first group captures the result, the second group captures the remainder
$1#$2$2$2$2$2      The result is put in place of the old number, the remainder passes to the next digit (so it is multiplied by 10) and is divided by two there -> 5 times the remainder goes to the next digit

这种替换足以将一个数字除以2,如果原始数字是奇数,我们只需要从末尾删除可能的.5s。

因此,这是完整的代码,我们不断除以2,直到数字中仍然有数字为止,并n在每次迭代时在字符串前放置一个常量:最后的数字n是结果。

.                  |
$*1#               Convert to digitunary
{`^(.*1)           Loop:|
n$1                    add an 'n'
(1*)(1?)\1#            |
$1#$2$2$2$2$2          divide by 2
)`#1*$                 |
#                      erase leftovers
n                  Return the number of 'n's in the string

在线尝试!


更新的解决方案,37字节

借助Martin Ender,大量重构了很多好的创意,这些创意大约占了三分之一。

主要思想是_用作我们的一元符号:通过这种方式,我们可以在字符串中使用常规数字,只要我们_在需要时将它们转换回s即可:这样可以节省除法和插入多个字节的字节数。数字。

这是代码:

<empty line>    |
#               put a # before each digit and at the end of the string 
{`\d            Loop:|
$*_                 Replace each digit with the corrisponding number of _
1`_                 |
n_                  Add an 'n' before the first _
__                  |
1                   Division by 2 (two _s become a 1)
_#                  |
#5                  Wherever there is a remainder, add 5 to the next digit
}`5$                |
                    Remove the final 5 you get when you divide odd numbers
n               Return the number of 'n's in the string

在线尝试!


1
我使用了类似的数字形式(但称为Unary-Coded Decimal),这对于使用Sed进行算术非常方便。
Toby Speight

11

Ruby,19个16字节

->n{"%b"%n=~/$/}

感谢约旦打高尔夫球3个字节


您可以使用%:保存一个字节->n{("%b"%n).size}
约旦

3
等等,这是更短:->n{"%b"%n=~/$/}
约旦

10

Jolf,2个字节

lB

只需转换为二进制,然后找到长度即可。



10

JavaScript ES6,19个字节

a=>32-Math.clz32(a)

Math.clz32返回数字的32位二进制表示形式中的前导零位的数量。因此,要获得所需的位数,我们要做的就是从32中减去该位数

f=
  a=>32-Math.clz32(a)
  
pre {
    display: inline;
}
<input id=x type="number" oninput="y.innerHTML = f(x.value)" value=128><br>
<pre>Bits needed: <pre id=y>8</pre></pre>


2
备选方案a=>1+Math.log2(a)|0也是19个字节。
尼尔

5
@尼尔1+...|0尖叫减去波浪号a=>-~Math.log2(a)是18
edc65 '16

@ edc65我数了17 ...但是,是的,我确定我错过了一些东西,感谢您指出。
尼尔

@Neil随意将其发布为单独的答案。它使用的方法与我的答案不同,因此使用您的方法来减少字节数会感到不公平
Bassdrop Cumberwubwubwub

10

bash / Unix工具,16字节

dc<<<2o$1n|wc -c

将其保存在脚本中,然后将输入作为参数传递。将打印以二进制表示该数字所需的位数。

这是一个解释:

dc是基于堆栈的计算器。其输入(解析为令牌)为:

2 —将2推入堆栈。

o —从堆栈中弹出一个值(为2)并将其作为输出基数(因此输出现在为二进制)。

bash程序的参数值($ 1)—将该参数压入堆栈。

n —从堆栈中弹出一个值(这是输入数字)并打印(以二进制形式,因为这是输出基数),并且没有尾随换行符。

因此,dc命令以二进制形式输出数字。

dc的输出通过-c选项通过管道传递到命令wc,该命令将在其输入中打印字符数。

最终结果是打印参数的二进制表示形式中的位数。


不错的语言选择,但是如果您附上说明,它会更酷。
NH。

@NH谢谢。我添加了一个解释。
米切尔·史派克

9

Google表格,15字节

从单元格获取输入A1并输出到保存公式的单元格

=Len(Dec2Bin(A1

要么

=Int(1+Log(A1,2

要么

=Int(Log(2*A1,2

Excel,17字节

与上述相同,但格式为MS Excel

=Len(Dec2Bin(A1))

要么

=Int(1+Log(A1,2))

要么

=Int(Log(2*A1,2))



8

C#,63 45 31字节

感谢Loovjo和TuukkaX,节省了18个字节

感谢Grax,节省了14个字节

 b=>1+(int)System.Math.Log(b,2);

它使用十进制数n具有⌊log2(n)⌋+ 1位,这在页上进行了描述:

特定十进制整数的位数

当2 ^(b-1)≤n≤2 ^ b – 1时,正整数n具有b位。例如:

  • 29具有5位,因为16≤29≤31或2 ^ 4≤29≤2 ^ 5 – 1
  • 123具有7位,因为64≤123≤127,或2 ^ 6≤123≤2 ^ 7 – 1
  • 967有10位,因为512≤967≤1023,或2 ^ 9≤967≤2 ^ 10 – 1

对于较大的数字,您可以查阅2的幂的表以查找包含您的数字的连续幂。

要了解为什么这样做有效,请考虑一下整数2 ^ 4到2 ^ 5 – 1的二进制表示形式。它们是10000至11111,所有可能的5位值。

使用对数

可以用另一种方式表示上述方法:位数是比您的数字大2的最小幂。您可以在数学上表示为:

bspec =⌊log2(n)⌋+ 1

该公式包括三个部分:

  • log2(n)表示n的以2为底的对数,对数是2乘以得到n的指数。例如,log2(123)≈6.9425145。小数部分的存在意味着n在2的幂之间。

  • ⌊x⌋是x的底数,是x的整数部分。例如,⌊6.9425145⌋=6。您可以将log2(n)⌋视为n的二进制表示形式中2的最高幂的指数。

  • +1将指数乘以2的下一个更高的幂。您可以将这一步骤考虑为二进制数的第2 ^ 0位,然后便得出其总位数。在我们的示例中,它是6 +1 =7。您可能会想使用上限函数⌈x⌉,它是大于或等于x的最小整数—这样计算位数:

bspec =⌈log2(n)⌉

但是,当n为2的幂时,这将失败。


您那里有多余的空间...)+ 1)...-> ...)+1...。另外,我认为您可以直接返回该值,而不必打印它。
Loovjo

您可以通过执行以下操作b=>1+(int)System.Math.Log(b,2); 将其降至31 :int转换提供与Math.Floor相同的输出,并且如果只引用一次System,则不需要using语句。
Grax32

6

C#,32个字节

n=>Convert.ToString(n,2).Length;

将参数转换为二进制字符串并返回字符串的长度。


4

Haskell,20个字节

succ.floor.logBase 2

组成一个以2为底,对数为底并加1的函数。


4

Befunge-9323 21个字节

&>2# /# :_1>+#<\#1_.@

Befunge是一种基于2D网格的语言(尽管我只使用一行)。

&                      take integer input
 >2# /# :_             until the top of the stack is zero, halve and duplicate it
          1>+#<\#1_    find the length of the stack
                   .@  output that length as an integer and terminate the program

在线尝试!


@JamesHolderness谢谢,我想可以将其缩短,因为它具有太多的哈希/空间,但是我不太明白。
JayDepp





3

QBIC,18个字节

:{~2^q>a|_Xq\q=q+1

真是不可思议的迈克!但是它是如何工作的呢?

:        Read input as integer 'a'
{        Start an infinite DO-LOOP
~2^q>a   If 2 to the power of q (which is 1 by default) is greater than a
|_Xq     Then quit, printing q
\q=q+1   Else, increment q
[LOOP is closed implicitly by QBIC]

3

Java 8 8,34 27字节

Java曾经有一些有用的内置函数!现在,我们只需要一些短名称...

x->x.toString(x,2).length()

在线尝试!

当然,您可以在没有内置函数的情况下执行此操作(请参阅Snowman的答案),但是要获得更大的字节数。


3

八度,19字节

@(x)nnz(dec2bin(x))    % or
@(x)nnz(de2bi(x)+1)    % or
@(x)nnz(de2bi(x)<2)    % or
@(x)numel(de2bi(x))    % or
@(x)rows(de2bi(x'))

八度有两个功能可将十进制数转换为二进制数。

dec2bin将数字转换为字符10(ASCII值48and 49)的字符串。除非另有说明,否则字符串的长度将等于所需的位数。由于10是非零字符,因此我们可以使用nnz来查找这样的元素数量:@(x)nnz(dec2bin(x))。这是19个字节,因此与Luis Mendo的另一个Octave答案相关

我们可以做得更好de2bi吗?

de2bi是返回二进制数具有编号的矢量的功能10为整数,而不是字符。de2bi显然比短2个字节dec2bin,但是我们不能再使用了nnz。我们可以使用nnz是否将1所有元素相加,或使其变成仅包含true值的逻辑向量。@(x)nnz(de2bi(x)+1)@(x)nnz(de2bi(x)<2)均为19个字节。使用numel还将给我们19个字节,@(x)numel(de2bi(x))

rows比短1个字节numel,但de2bi返回一个水平向量,因此必须进行转置。@(x)rows(de2bi(x)')恰好也是19个字节。



2

视网膜  44  23字节

需要太多内存才能运行较大的输入值。转换为一元,然后重复除以2,计算直到达到零为止的次数。字节数假定为ISO 8859-1编码。

.*
$*
+`^(1+)1?\1
$1_
.

在线尝试


1
我不确定这是否有效。这不是“需要的内存比您可能拥有的更多”的情况,而是“它需要的内存比Retina本身无法处理的更多”。特别是,由于Retina的实现存在局限性,对于2 ^ 30或更高阶的输入,初始转换为一元将失败。
马丁·恩德

如果有效,则可以缩短很多时间:tio.run/nexus/retina#@6@nxaWixaWdEKdhqK1paB9jyKViGM@l9
Martin Ender
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.