完全相同的Python代码在更快的机器上慢20倍?

Had*_*han 5 python performance

我正在尝试执行以下 python 代码,该代码将按字母顺序返回“ABCDEGGHIJK”的第一个排列,这将采用非常简单的排序算法,如 Project Euler Problem 336 中定义的最大迭代次数进行排序。

这是代码(对错误的变量名称表示歉意):

from itertools import permutations

def first_out_letter(st):
    """
    returns the first letter alphabetically  in st which is not in    sorted order
    alphabetically, string must be all in captials.
    """
    def first(string):
        """
        returns the first alphabetical letter in a string, only capitals allowed
        """
        alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

        for i in alpha:
            if i in string:
                return i
        return None
    sor = ''.join(sorted(st))
    for i in range(len(st)):
        if st[i] != sor[i]:
            return first(st[i:])
    return None

def get_arrangement_size(arrang,dictionary):
    """
     returns the number of shifts needed to arrange a string in lexographic order
    using a dumb method of first getting the first digit correct, then the second
    and so on...

    the argument dictionary stores precomputed results and is modified during execution. 
    """
    if arrang in dictionary.keys():
        return dictionary[arrang]
    sor = ''.join(sorted(arrang))
    if arrang == sor:
        dictionary[arrang] = 0
        return 0
    else:
        bing = first_out_letter(arrang)
        num_arr = 0
        pos_bing = 0
        for i in range(len(arrang)):
            if arrang[i] == sor[i]:
                num_arr += 1
            else:
                break
        for i in range(len(arrang)):
            if arrang[i] != bing:
                pos_bing += 1
            else:
                break
        if bing == arrang[-1]:
            low = get_arrangement_size(arrang[:num_arr]+arrang[num_arr:][::-1],dictionary)
        else:
            low = get_arrangement_size(arrang[:pos_bing]+arrang[pos_bing:][::-1],dictionary)
        dictionary[arrang] = low+1            
        return low+1

solutions = {}
letters = ["A","B","C","D","E","F","G","H","I","J","K"]
piv = permutations(letters)
for item in piv:
    get_arrangement_size(''.join(item),solutions) #builds up the solutions dictionary
ma = max(solutions.values())
fir = []
for item in solutions.keys():
    if solutions[item] == ma:
        fir.append(item)
fir = sorted(fir)
print(fir[0])
Run Code Online (Sandbox Code Playgroud)

该代码在我的两台机器上运行良好,并给出了正确的答案,但我发现两台机器上的速度差异非常大,最多可达 20 倍。

我的(理论上)更快的 i5 计算机正在运行 Linux Mint 和 python 2.7.6,并且也有更多的内存,但是当我运行此代码时,我发现它的执行速度比我的较慢的计算机(带有 Windows 和 python 的 Celeron)慢得多3.5.1. 当我在两台机器上运行此代码时,我没有同时运行其他任何东西,并且它们都使用相同的 IDE (Spyder),所以我不知道为什么会有这种速度差异?

任何帮助或解释这一点的理由将不胜感激。

编辑:根据 Chriss 的建议,我尝试在速度较慢的计算机上在 python 2.7 上运行此代码,它也比我在同一台计算机上在 3.5 上运行代码时慢得多。所以这个差异是由 python 版本引起的,但到底是什么导致了我不知道并且仍然想知道的差异。

nie*_*mmi 5

dict.keys()这是由Python 2 和 3 之间的差异引起的。在 Python 2 中dict.keys(),将创建键的副本作为列表并返回它。在Python 3dict.keys()中将返回dictionary view类似set对象的内容。检查是否可以从中找到元素list比检查元素是否在其中慢得多,这set解释了差异。

如果您进行以下更改,代码在 Python 2 和 3 上的运行时间大致相同:

if arrang in dictionary: # Instead of if arrang in dictionary.keys()
    return dictionary[arrang]
Run Code Online (Sandbox Code Playgroud)

  • @AbdulHadiKhan:是的,您可以使用它,但它不会比仅“dictionary.values() 中的元素”更快。生成“set”和检查值是否在“list”中的时间复杂度都是“O(n)”。如果您需要快速检查键和值,最好将值存储到单独的容器中,例如 [`Counter`](https://docs.python.org/2/library/collections.html#collections.Counter) (如果您知道值是唯一的,则“set”也可以)。 (2认同)