有没有一种有效的方法来更改列表中的所有元素,使最小的数字变为零,并且后面的每个数字都有整数间距?

Is there an efficient way to change all the elements in a list such that the lowest number becomes a zero, and each number after has integer spacing?

我一般想实现以下目标,理想情况下不使用 for 循环来遍历每个元素,因为这看起来效率不高。列表的每个元素都将是 int 类型。

3 个示例:

In  [1]: L1 = [3,6,19,6,3,3,19]
In  [2]: f(L1)
Out [2]: [0,1,2,1,0,0,2]

In  [3]: L2 = [0,1,2,4,1,4,1,0]
In  [4]: f(L2)
Out [4]: [0,1,2,3,1,3,1,0]

In  [5]: L3 = [2,3,3,4,2,2,3,4]
In  [6]: f(L3)
Out [6]: [0,1,1,2,0,0,1,2]

数字的顺序并不重要,但相对频率很重要。

如果数字之间没有空格,我目前的尝试是有效的——就像在 L3 中一样。

我的尝试:

def f(L):
    L = np.array(L) - min(L)
    return list(L)

但这显然没有按照我想要的方式处理差异。如果我可以避免对 L 的每个元素使用 for 循环并且也避免进行元素明智的比较(比如检查 L1 中 19 之后的下一个最小数字是否是 6)那将是理想的,但对我来说并不明显有一种方法可以避免这种情况。

我认为您不可能在 O(n) 时间内完成此操作,因此试图避免 for 遍历列表是没有用的。这是我将采用的方法:

>>> def f(nums):
...     m = {num: i for i, num in enumerate(sorted(set(nums)))}
...     return [m[num] for num in nums]
...
>>> f([3,6,19,6,3,3,19])
[0, 1, 2, 1, 0, 0, 2]
>>> f([0,1,2,4,1,4,1,0])
[0, 1, 2, 3, 1, 3, 1, 0]
>>> f([2,3,4,4,2,2,3,4])
[0, 1, 2, 2, 0, 0, 1, 2]

请注意,排序是 O(m log m),其中 m 是唯一数字的数量,因此在最坏的情况下这是 O(n log n)。

请注意,如果您实际使用 numpy 开始,则可以将 numpy.uniquereturn_inverse=True 参数一起用于此特定因式分解:

>>> np.unique(L1, return_inverse=True)
(array([ 3,  6, 19]), array([0, 1, 2, 1, 0, 0, 2]))

这应该非常高效,但您应该针对您的特定 use-case 进行分析。请注意,这具有您想要的行为,因为 np.unique 将 return 排序 数组的唯一元素。