2012-09-29 103 views
2

我試圖編寫一些計算2的冪的代碼,但將它們存儲在具有10的冪作爲鍵的字典中,所以例如2^9將被存儲爲如果條目不存在,將條目添加到字典中

{0:2, 1:1, 2:5} 

其中您有5 * 10^2 + 1 * 10^1 + 2 * 10^0。

所以目前,我有一些像

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 

的問題是

result.setdefault(p+1,result[p]/10) 

這將覆蓋任何在result[p+1]。我知道只要檢查字典並根據需要「初始化」新密鑰是可能的,但是有沒有更好的方法可以實時執行?我可以將結果初始化爲足夠長的時間,但由於我不一定知道「足夠長」多長時間,因此在我看來,讓它在飛行中更有意義。

基本上我想沿線的

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 

東西任何幫助,非常感謝!

+0

你的代碼張貼在設置'結果[P]',而不是'結果[p + 1]'。 – Amber

+0

快速提問:爲什麼您使用字典'{0:2,1:1,2:5}'而不是列表'[2,1,5]'?無論哪種方式,'x [0] == 2','x [1] == 1','x [2] == 5'。 – abarnert

+0

@Amber:抱歉,我不太明白,我的目標是將{0:16}設置爲{0:6,1:1},所以我必須同時更改... – acs

回答

2

只有result[p] < 10 Python的2或result[p] == 0所以,你每次通過主環路做什麼:雙每個數字,然後再進行額外的幾十到下一個數字。問題在於,對於最高位數字,下一位數字不存在。

所以,這裏有一個解決方案 - 雖然它不適用於除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 

從原始代碼有兩個關鍵的變化。

首先,我們必須添加[:]到第二result.keys()結束,在循環之前遍歷字典的一組密鑰的副本,而不是當前的密鑰。原因是,如果最後一位數字大於5,我們將添加一個新的密鑰到字典中,並且在迭代時不允許這樣做。 (爲什麼?有幾個原因,但最簡單的是詞典是以任意順序排列的,每次添加一個鍵時,整個順序都可以改變,這在以後也會變得很重要。)

二,原始問題:如何避免檢查if p+1 in result以決定是否存儲r_p或將r_p添加到現有值?

當您處理計數時,您使用collection.Counter,它與dict類似,但它只存儲整數,並且任何缺少的鍵值都爲0。否則,您通常會使用collection.defaultdict,它與dict類似,不同之處在於您可以指定所有缺少的鍵都具有的默認值。通過Counterdefaultdict,您只需要result[p+1] += result[p]/10

但我已經使用了另一種方法:dict上的get方法,該方法允許您指定默認值。我會解釋爲什麼後面,但請記住,一般來說,當您發現自己達到get時,您可能需要defaultdictCounter

所以,現在它會運行和工作,爲2的冪,但它不會對其他大國合作,原因有二:

首先,我們正在做的攜帶隨機順序。對於2的冪,你能攜帶的最多的是1,並且1不能影響下一個數字是否攜帶。 (8不會攜帶,8 + 1也不會,現在有辦法得到9)。但對於任何其他力量,這是不正確的。 (在CPython 2.7和3.2中),當你從一個空的dict開始並添加少量的小鍵時(實際上,小鍵散列值,但小的ints散列到自己)以排序順序,並且不要刪除任何東西,dict將經常迭代(和打印)的順序。但總的來說,訂單是不可預測的,你不能依賴它。

該問題的解決方案是使用collections.OrderedDict,它按照添加它們的順序遍歷按鍵。這就是爲什麼我不想使用defaultdictCounter:因爲如果您必須切換到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) 
+0

非常感謝您的精心解答!這裏有很多概念要經過...... – acs

1

您可以通過使用in語法檢查的一個關鍵的存在:

for p in result.keys()[:]: 
    r_p = result[p]/10 

    if r_p != 0: 
     if p + 1 in result: 
      result[p + 1] += r_p 
     else: 
      result[p + 1] = r_p 

或者,你可以使用Counter,它會自動完成:

from collections import Counter 

result = Counter() 

for p in result.keys()[:]: 
    r_p = result[p]/10 

    if r_p != 0: 
     result[p + 1] += r_p 

而且,你的條件是有點奇怪。 result[p]/10 == 0關於Python 3

+0

+1爲'計數器'。但是第一個版本會更簡單:'result [p + 1] = result.get(p + 1,0)+ r_p'而不是'if:'/'else:'循環。 – abarnert

+0

@abarnert:謝謝。我應該更頻繁地開始使用'.get()'。 – Blender

+0

好吧,你有一半時間到達'get'你意識到'counter','defaultdict'或者'try:'/'d [key] ...'/'除了:'無論如何是更好的答案...... – abarnert