我在数学论坛上看到一个问题,一个人正在讨论一遍又一遍地将数字加起来直到达到一位数。 (即“362”将成为“3 + 6 + 2”,将成为“11”......然后“11”将成为“1 + 1”将成为“2”因此“362”将返回2 ...我写了一些很好的代码来得到答案并发布它只是为了超出一个用户提出模数9中的任何数字等于这个“无限数字总和”,我检查了他是对的......好吧几乎是正确的,如果返回零,你必须用“9”将其切换出来,但这是一个非常快速的修复......
362 = 3 + 6 + 2 = 11 = 1 + 1 = 2
...或
362%9 = 2
Anways,mod9方法非常适合无限添加数字之和,直到你只剩下一个数字......但是只做一次(即362只返回“11”)...有人能想到快速算法吗?
答案 0 :(得分:5)
这是一个很酷的技巧,可以将二进制中的1位数和一个固定宽度的整数相加。在每次迭代中,您将每个数字的一半数字分成两个值,向下移位一个值,然后添加。第一次迭代,分开其他数字。第二次迭代,数字对,等等。
鉴于27是00011011为8位二进制,过程是......
00010001 + 00000101 = 00010110 <- every other digit step
00010010 + 00000001 = 00010011 <- pairs of digits
00000011 + 00000001 = 00000100 <- quads, giving final result 4
你可以用十进制做类似的技巧,但它的效率低于一个简单的循环,除非你有一个十进制数的直接表示,快速操作将所选数字归零并进行数字移位。所以对于12345678你得到......
02040608 + 01030507 = 03071115 <- every other digit
00070015 + 00030011 = 00100026 <- pairs
00000026 + 00000010 = 00000036 <- quads, final result
所以1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 = 36,这是正确的,但如果您的数字表示是固定宽度小数,则只能有效地执行此操作。它总是需要lg(n)次迭代,其中lg表示基数为2的对数,并且向上舍入。
为了进一步扩展(基于评论中的讨论),让假装这是理智的,有点......
如果计算一位数的加法,实际上更多的工作比这里的简单循环更重要。与计数位的按位技巧一样,这个想法是重新排序这些加法(使用关联性),然后并行计算尽可能多的加法,使用单个全宽加法来实现两个半宽加法,四个数字清除和数字移位操作会产生很大的开销,如果将其实现为循环(计算或查找每个数字屏蔽和移位距离值),则会产生更多开销。步)。 &#34;循环&#34;应该完全展开,并将这些掩码和移位距离作为常量包含在代码中以避免这种情况。
支持Binary Coded Decimal (BCD)的处理器可以处理此问题。数字屏蔽和数字移位将使用位屏蔽和位移来实现,因为每个十进制数字将以4(或更多)位编码,与其他数字的编码无关。
一个问题是,如今BCD支持非常少见。它曾经在8位和16位日内相当普遍,但据我所知,仍然支持它的处理器现在这样做主要是为了向后兼容。原因包括......
很早的处理器并没有包含硬件乘法和除法。对这些操作的硬件支持意味着现在将二进制转换为十进制更容易,更有效。二进制现在几乎用于所有事情,BCD几乎被遗忘。
库中有十进制数字表示,但是很少有高级语言能够为硬件BCD提供便携式支持,所以由于汇编程序停止成为大多数开发人员的真实选项,因此BCD支持只是停止使用。
随着数字越来越大,即使打包的BCD打包也非常低效。数字表示基数10 ^ x具有基数10的最重要属性,并且很容易解码为十进制。基本1000只需要每三位数10位,而不是12位,因为2 ^ 10是1024.这足以显示你得到32位的额外十进制数字 - 9位而不是8位 - 而且你已经还剩下2位,例如为了一个标志位。
问题是,对于这个数字总计算法来说,你需要使用可能至少32位(8位)的固定宽度小数。这给了12个操作(6个掩码,3个移位,3个加法),而不是(完全展开的)简单循环的15个加法。但这是一个临界收益 - 代码中的其他问题很容易意味着它实际上更慢。
64位(16位十进制数字)的效率增益更清晰,因为它仍然只有16个操作(8个掩码,4个移位,4个加法)而不是31个,但找到支持64的处理器的几率比特BCD操作似乎很渺茫。即使你这样做了,你还需要多久一次?似乎不太可能值得努力和失去便携性。
答案 1 :(得分:1)
以下是Haskell中的内容:
sumDigits n =
if n == 0
then 0
else let a = mod n 10
in a + sumDigits (div n 10)
哦,但我刚看到你已经这样做了......
(然后还有明显的:
sumDigits n = sum $ map (read . (:[])) . show $ n
)
答案 2 :(得分:1)
对于简短代码,请尝试以下方法:
int digit_sum(int n){
if (n<10) return n;
return n%10 + digit_sum(n/10);
}
或者,用语言来说,
- 如果数字小于10,那么数字和就是数字本身。
- 否则,数字和是当前的最后一位(也就是n mod10或n%10),再加上该数字左边所有数字的总和(n除以10,使用整数除法)。
- 这个算法也可以推广到任何基数,用基数代替10。
答案 3 :(得分:0)
int digit_sum(int n)
Do
if (n<10) return n;
Exit do
else
n=n%10 + digit_sum(n/10);
Loop