【问题标题】:How many digits in this base?这个基数有多少位?
【发布时间】:2009-12-04 14:05:04
【问题描述】:

问题是推导出一个公式来确定给定十进制数在给定基数中的位数。

例如:十进制数 100006 可以分别以 2、3、4、5、6、7、8 为基数的 17、11、9、8、7、6、8 位表示。强>

到目前为止,我得出的公式是这样的:(log10(num) /log10(base)) + 1。

在 C/C++ 中,我使用这个公式来计算上面给出的结果。

long long int size = ((double)log10(num) / (double)log10(base)) + 1.0;

但遗憾的是,在某些情况下,公式没有给出正确答案,例如:

Number 8 in  base 2 : 1,0,0,0
Number of digits: 4
Formula returned: 3

Number 64 in  base 2 : 1,0,0,0,0,0,0
Number of digits: 7
Formula returned: 6

Number 64 in  base 4 : 1,0,0,0
Number of digits: 4
Formula returned: 3

Number 125 in  base 5 : 1,0,0,0
Number of digits: 4
Formula returned: 3

Number 128 in  base 2 : 1,0,0,0,0,0,0,0
Number of digits: 8
Formula returned: 7

Number 216 in  base 6 : 1,0,0,0
Number of digits: 4
Formula returned: 3

Number 243 in  base 3 : 1,0,0,0,0,0
Number of digits: 6
Formula returned: 5

Number 343 in  base 7 : 1,0,0,0
Number of digits: 4
Formula returned: 3

所以错误是 1 位数。我只是希望有人帮助我更正公式,以便它适用于所有可能的情况。

编辑:根据输入规范,我必须处理像 10000000000 这样的情况,即 10^10,我不认为 C/C++ 中的 log10() 可以处理这种情况?因此,对于此问题的任何其他程序/公式都将受到高度赞赏。

【问题讨论】:

  • 看起来你在边缘情况下遇到了一个问题。
  • 我以前在学校用我的计算器做这个;我忘记了我当时使用的公式,但是当我被教 log() 函数时,我就像,哇,这要简单得多!
  • 是的,但无法确定所需的修改。
  • int size = static_cast<int>((log10((double)num) / log10((double)base)) + 1.000001);
  • 这不是我的作业+公式标签是必要的,所以回滚!

标签: c++ c algorithm math formula


【解决方案1】:

您的编译器设置中有快速浮动操作。您需要精确的浮动操作。问题是 log10(8)/log10(2) 在数学上总是 3。但可能你的结果是 2.99999,例如。这是坏的。您必须添加少量添加剂,但不能添加 0.5。它应该是大约 .00001 或类似的东西。

几乎是真的公式:

int size = static_cast<int>((log10((double)num) / log10((double)base)) + 1.00000001);

真正的解决方案

您应该检查公式的结果。复杂度为O(log log n)O(log result)

int fast_power(int base, int s)
{
    int res = 1;
    while (s) {
        if (s%2) {
            res*=base;
            s--;
        } else {
            s/=2;
            base*=base;
        }
    }
    return res;
}

int digits_size(int n, int base)
{
    int s = int(log10(1.0*n)/log10(1.0*base)) + 1;
    return fast_power(base, s) > n ? s : s+1;
}

此检查优于使用 base 乘法的蛮力测试。

【讨论】:

  • 谢谢,但是如果 num = 10000000000l 呢?我认为 log10() 无法处理这种情况,还有其他解决方案吗?
  • +1,我没有使用编译器检查你的解决方案,但我认为它会正常工作。我喜欢你的方法。虽然我知道 O(logn) 的幂运算但不知道它在这里使用,所以谢谢:)
  • 我接受您的解决方案,因为它解决了编辑前和编辑后的两个问题。
【解决方案2】:

以下任何一种都可以:

>>> from math import *
>>> def digits(n, b=10):
...     return int(1 + floor(log(n, b))) if n else 1
...
>>> def digits(n, b=10):
...     return int(ceil(log(n + 1, b))) if n else 1
... 

第一个版本在mathpath.org 进行了解释。在第二个版本中,+ 1 是任何数字 n 的正确答案,该数字是具有 d 位基数的最小数字 b。也就是说,那些以 b 为基数写成 10...0 的数字。注意输入 0 必须被视为特殊情况。

十进制示例:

>>> digits(1)
1
>>> digits(9)
1
>>> digits(10)
2
>>> digits(99)
2
>>> digits(100)
3

二进制:

>>> digits(1, 2)
1
>>> digits(2, 2)
2
>>> digits(3, 2)
2
>>> digits(4, 2)
3
>>> digits(1027, 2)
11

编辑:OP 声明log 解决方案可能不适用于大输入。我不知道,但如果是这样,下面的代码不应该分解,因为它只使用整数算术(这次是在 C 中):

unsigned int 
digits(unsigned long long n, unsigned long long b)
{
  unsigned int d = 0;
  while (d++, n /= b);
  return d;
}

这段代码可能效率较低。 是的,它是为最大模糊点而写的。它只是使用观察每个数字至少有一个数字,并且 b 的每个除法不产生 0 意味着存在一个额外的数字。更易读的版本如下:

unsigned int 
digits(unsigned long long n, unsigned long long b)
{
  unsigned int d = 1;
  while (n /= b) {
    d++;
  }
  return d;
}

【讨论】:

  • 这在更大的数字上会失败——对我来说,digits(1027, 2),但它可能取决于实现。
  • @Beta:很有趣。这里digits(1027, 2) 产生11,这是正确的(可以用例如len('{0:b}'.format(1027)) 进行测试)。
  • 你的日志函数的精度是多少?
  • Python 自动切换到任意精度算术; log 永远是正确的。但是floorceil 不是:至少在我的计算机上,打印出 10 的前 20 次方的数字(n)会显示错误。 (第一个版本中有两个 3,第二个版本中有两个 15。)对于 C 版本:为什么在 for 循环中使用逗号分隔语句和一个空主体的诡计?您可以将其中一个移出并提高清晰度。或者,如果清晰不是目标,写成:“while(n/=b) d++;”会更短。 :-)
  • 我没有完全剖析你的第二个解决方案,但只是粗略地看了一下,在我看来你只是在计算数字的整个基本表示,效率不高:)
【解决方案3】:
【解决方案4】:

由于您的公式是正确的(我刚刚尝试过),我认为这是您的除法中的舍入错误,导致数字略小于应有的整数值。因此,当您截断为整数时,您会失去 1。尝试在最终值上再加 0.5(这样截断实际上是一个舍入操作)。

【讨论】:

  • 您的意思是:size = ((((double)log10(num) / (double)log10(base))) + 1.0)+ 0.5;?那么它就行不通了。
  • 如果您只添加另一个 0.5,您将在其他边界情况下得到不同的答案:以 2 为底的数字 15 : 1,1,1,1 (log10(15) / log10(2) ) + 1.5 = 5.407..,所以答案是 5,而不是 4。
  • @kigurai : size = ceil((log10(num) / log10(base)) + 1.0); 不行!!
  • 正确的公式是floor(log10(num)/log10(base)+1.0)。但是,由于初始除法中的舍入错误,该公式在实践中不一定有效。因此,当且仅当您在整数的某个 epsilon 范围内时才需要四舍五入,然后取整,因为某些除法会正确地给您一个非整数答案。
  • @debanjan,是的,我注意到它不适用于 n=7 和 base=2。
【解决方案5】:

你想要的是上限(=不大于的最小整数)logb(n+1),而不是你现在正在计算的,下限(1+logb (n))。

你可以试试:

int digits = (int) ceil( log((double)(n+1)) / log((double)base) );

【讨论】:

  • 当然,如果 n
  • 这还不够,因为它仍然会为n = 100base = 10 生成2
  • 如果编译器的设置中有快速浮动操作并且n=7,base=2,它不起作用log10(8)/log10(2) 大约是 32.99993.000001)。并且 ceil(3.00001) 有时可能是 4。
【解决方案6】:

正如其他人指出的那样,您有舍入误差,但建议的解决方案只是移动危险区域或使其更小,并不能消除它。如果您的数字是整数,那么您可以验证 -- 使用整数算术 -- 底的一个幂小于或等于您的数字,而下一个在它之上(第一个幂是位数)。但是,如果您在链中的任何位置使用浮点运算,那么您将很容易出错(除非您的基数是 2 的幂,甚至可能是这样)。

编辑:
这是整数运算中粗略但有效的解决方案。如果您的整数类可以容纳与 base*number 一样大的数字,那么这将给出正确答案。

大小 = 0,k = 1; 而(k&lt=num) { k *= 基数; 大小 += 1; }

【讨论】:

    【解决方案7】:

    使用你的公式,

    log(8)/log(2) + 1 = 4
    

    问题在于对数计算的精度。使用

    ceil(log(n+1)/log(b)) 
    

    应该可以解决这个问题。这和

    不太一样
    ceil(log(n)/log(b)) 
    

    因为这给出了 n=8 b=2 的答案 3,也不等于

    log(n+1)/log(b) + 1
    

    因为这给出了 n=7 b=2 的答案 4(当计算到全精度时)。

    我实际上得到了一些奇怪的结果,用 g++ 实现和编译第一个表单:

    double n = double(atoi(argv[1]));
    double b = double(atoi(argv[2]));
    int i = int(std::log(n)/std::log(b) + 1.0);
    

    失败(IE 给出答案 3),而,

    double v = std::log(n)/std::log(b) + 1.0;
    int i = int(v);
    

    成功(给出答案 4)。再看一下我认为是第三种形式

    ceil(log(n+0.5)/log(b)) 
    

    会更稳定,因为它避免了当 n(或第二种形式的 n+1)是 b 的整数幂(对于 n 的整数值)时的“关键”情况。

    【讨论】:

    • 你的日志函数的精度是多少?
    • 我使用了一个 JavaScript 计算器,它给出了第一种形式的正确答案,我认为 Sixlettervariables 得到错误答案的原因是因为代表最终结果的问题。特别是 log(8)/log(2) 正好是 3 (2^3 = 8),所以 log(8)/log(2)+1 的期望值为 4。
    【解决方案8】:

    将舍入函数(例如 + 0.5)包装到代码中的某处可能是有益的:除法很可能产生(例如)2.99989787,在其中添加 1.0,得到 3.99989787,然后将其转换为 int ,它给出 3。

    【讨论】:

      【解决方案9】:

      看起来这个公式对我来说是正确的:

      Number 8 in  base 2 : 1,0,0,0
      Number of digits: 4
      Formula returned: 3
      
      log10(8) = 0.903089
      log10(2) = 0.301029
      
      Division => 3
      
      +1 => 4
      

      所以这肯定只是一个舍入误差。

      【讨论】:

        【解决方案10】:

        浮点舍入问题。

        log10(216) / log10(6) =  2.9999999999999996
        

        但您不能按照建议添加 0.5,因为它不适用于以下情况

        log10(1295) = log10(6) = 3.9995691928566091   //    5, 5, 5, 5
        log10(1296) = log10(6) = 4.0                  // 1, 0, 0, 0, 0
        

        也许使用 log(value, base) 函数可以避免这些舍入错误。

        【讨论】:

          【解决方案11】:

          我认为消除舍入误差而不产生其他错误的唯一方法是使用或实现整数对数。

          【讨论】:

            【解决方案12】:

            这是 bash 中的解决方案:

            % digits() { echo $1 $2  opq | dc | sed 's/ .//g;s/.//' | wc -c; }
            
            
            % digits 10000000000 42
            7
            

            【讨论】:

              【解决方案13】:
              static int numInBase(int num, int theBase)
              {
                 if(num == 0) return 0;
                 if (num == theBase) return 1;
                 return 1 + numInBase(num/theBase,theBase);
              }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2015-03-19
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2021-12-04
                • 1970-01-01
                相关资源
                最近更新 更多