所以,你每次通过主循环所做的是:将每个数字加倍,然后将额外的十位带到下一个数字。问题是,对于最高数字,下一个数字不存在。
所以这是一个解决方案——尽管它不适用于除 2 之外的任何幂,原因我将在下面讨论。
def foo():
result={0:2}
for i in range(2,10):
for p in result.keys():
result[p]*=2
for p in result.keys()[:]:
if result[p] / 10 != 0:
result[p+1] = result.get(p+1, 0) + result[p] / 10
result[p] = result[p] % 10
您的原始代码有两个关键更改。
首先,我们必须添加[:]
到 second 的末尾result.keys()
,以便在循环之前迭代字典的一组键的副本,而不是它的当前键。原因是如果最后一位数字大于 5,我们将在字典中添加一个新键,并且在迭代它时不允许这样做。(为什么?有几个原因,但最简单的一个是字典是任意顺序的,每次添加一个键,整个顺序都会改变。这在以后也很重要。)
其次,您最初的问题是:您如何避免必须检查if p+1 in result
以决定是存储r_p
还是添加r_p
到现有值?
当您处理计数时,您使用collection.Counter
,它类似于 a dict
,只是它只存储整数,并且任何缺少的键的值都为 0。否则,您通常使用collection.defaultdict
,它类似于 a dict
,除了您可以指定一个默认值所有丢失的钥匙都有。使用 aCounter
或defaultdict
,您只需要result[p+1] += result[p]/10
.
但我使用了另一种选择: 上的get
方法dict
,它允许您指定默认值。我稍后会解释为什么,但请记住,一般来说,当你发现自己伸手去拿 时get
,你可能想要一个defaultdict
orCounter
代替。
所以,现在它可以运行并工作,对于 2 的幂。但它不适用于其他幂,原因有两个:
首先,我们以随机顺序进行进位。对于 2 的幂,您最多可以携带 1,并且 1 不会影响下一个数字是否携带。(8 不会携带,8+1 也不会,现在有办法得到 9。)但对于任何其他力量,这不是真的。
在简单的测试中,你可能不会注意到这一点,因为它恰好发生在(至少在 CPython 2.7 和 3.2 中)当你从一个空开始dict
并添加少量小键(实际上,具有小散列值的键,但ints 以排序顺序散列到自己),并且不删除任何内容,dict
通常会按顺序迭代(和打印)。但总的来说,顺序是不可预测的,你不能依赖它。
该问题的解决方案是使用collections.OrderedDict
,它按照添加顺序对键进行迭代。这就是为什么我不想使用defaultdict
or Counter
: 因为如果你必须切换到OrderedDict
,你唯一的选择是get
.
其次,一旦您的指数超过 10,您可能需要进位 100。这意味着您必须进位 10,这将导致下一个数字即使是 0 也会进位。这意味着我们不能只复制密钥进步。例如,假设我们正在计算 75 的幂。从 开始{0:5, 1:7}
。将每个数字乘以 75 即可{0:375, 1:525}
。现在携带 37: {0:5, 1:562}
。现在携带 56: {0:5, 1:2, 2:56}
。因为我们只是迭代原始密钥的副本——也就是说——[0, 1]
我们没有机会携带 5。
我会把它留给你来解决这个问题。
但是,您可能要考虑的一件事是创建一个新字典,而不是修改旧字典。然后你就可以解决所有这些问题。(作为一般规则,使用纯函数而不是改变状态会解决很多问题,但当然是以额外复制为代价的。)
def double(d):
result = Counter()
for p in d:
result[p] += d[p] % 10
result[p+1] += d[p] / 10
return result
digits={0:2}
for i in range(2,10):
for p in digits:
digits[p]*=2
digits = double(digits)