I’m trying to write some code that calculates powers of 2 but stores them in a dictionary that has powers of 10 as the key, so for example, 2^9 would be stored as
{0:2, 1:1, 2:5}
where you have 5*10^2 + 1*10^1 + 2*10^0.
So currently, I have something like
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.setdefault(p+1,result[p]/10)
result[p] = result[p] % 10
The issue is
result.setdefault(p+1,result[p]/10)
It overwrites whatever was in result[p+1]. I know it’s possible to just check the dictionary and then “initialize” the new key if needed, but is there a more elegant way to do it on the fly? I could initialize result to be long enough, but since I don’t necessarily know how long is “long enough”, it seems to me to make more sense to extend it on the fly.
Basically I’d like something along the lines of
for p in result.keys():
if result[p] / 10 != 0:
if result[p+1] exists:
result[p+1]+=result[p]/10
else:
create result[p+1] and set its value to result[p]/10
Any help is much appreciated!
So, what you’re doing each time through the main loop is: double each digit, then carry extra tens to the next digit. The problem is that, for the highest digit, the next digit doesn’t exist.
So here’s a solution—although it’s not going to work for any power other than 2, for reasons I’ll get to below.
There are two key changes from your original code.
First, we had to add
[:]to the end of the secondresult.keys(), to iterate over a copy of the dictionary’s set of keys before the loop, rather than its current keys. The reason is that if the last digit is >5 we’re going to be adding a new key to the dictionary, and you’re not allowed to do that while iterating over it. (Why? A few reasons, but the simple one is that dictionaries are in arbitrary order, and every time you add a key, the whole order can change. This will be important later, too.)Second, there’s your original question: How can you avoid having to check
if p+1 in resultto decide whether to storer_por addr_pto the existing value?When you’re dealing with counts, you use
collection.Counter, which is like adictexcept that it only stores integers, and any missing key has a value of 0. Otherwise, you usually usecollection.defaultdict, which is like adictexcept that you can specify an default value that all missing keys have. With either aCounterordefaultdict, all you’d need isresult[p+1] += result[p]/10.But I’ve used a different alternative: the
getmethod on thedict, which lets you specify a default value. I’ll explain why later, but keep in mind that in general, when you find yourself reaching forget, you may want adefaultdictorCounterinstead.So, now it’ll run, and work, for powers of 2. But it won’t work for other powers, for two reasons:
First, we’re doing the carries in random order. For powers of 2, the most you could carry is a 1, and there’s no way a 1 could affect whether the next digit up carries. (8 won’t carry and neither will 8+1, and there’s now way to get a 9.) But for any other power, that isn’t true.
In simple testing, you might not notice this, because it just so happens that (at least in CPython 2.7 and 3.2) when you start with an empty
dictand add a small number of small keys (actually, keys with small hash values, but small ints hash to themselves) in sorted order, and don’t delete anything, thedictwill often iterate (and print) in order. But in general, the order is not predictable, and you can’t rely on it.The solution to that problem is to use
collections.OrderedDict, which iterates over the keys in the order in which they were added. And that’s why I didn’t want to usedefaultdictorCounter: because if you have to switch toOrderedDict, your only option isget.Second, once your exponent gets over 10, you may need to carry 100. That means you have to carry the 10, which will cause the next digit to carry even if it’s 0. That means that we can’t just copy the keys in advance. For example, let’s say we’re doing powers of 75. Start off with
{0:5, 1:7}. Multiply each digit by 75 to{0:375, 1:525}. Now carry the 37:{0:5, 1:562}. Now carry the 56:{0:5, 1:2, 2:56}. Because we were only iterating over a copy of the original keys—that is,[0, 1]—we don’t get a chance to carry the 5.I’ll leave it up to you to fix that problem.
However, one thing you might want to consider is creating a new dictionary, instead of modifying the old one in place. Then you can get around all of these problems. (As a general rule, using pure functions instead of mutating state gets around a lot of problems, but of course at the cost of extra copying.)