Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/302.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python中移动和合并列表元素的最有效方法(2048)_Python_Algorithm_Optimization_Python 3.x - Fatal编程技术网

Python中移动和合并列表元素的最有效方法(2048)

Python中移动和合并列表元素的最有效方法(2048),python,algorithm,optimization,python-3.x,Python,Algorithm,Optimization,Python 3.x,我有一个函数,它基本上会右对齐列表,但也会将两个相等的元素合并为一个(列表将始终至少有一个元素): 以下是一些输入和输出示例: >>> shift([0, 0, 1, 0]) [0, 0, 0, 1] >>> shift([1, 1, 0, 0]) [0, 0, 0, 2] >>> shift([2, 0, 1, 0]) [0, 0, 2, 1] 最有效的方法是什么?如果对矩阵中的每一行执行此操作,是否有比执行以下操作更有效的方法: mat

我有一个函数,它基本上会右对齐列表,但也会将两个相等的元素合并为一个(列表将始终至少有一个元素):

以下是一些输入和输出示例:

>>> shift([0, 0, 1, 0])
[0, 0, 0, 1]
>>> shift([1, 1, 0, 0])
[0, 0, 0, 2]
>>> shift([2, 0, 1, 0])
[0, 0, 2, 1]
最有效的方法是什么?如果对矩阵中的每一行执行此操作,是否有比执行以下操作更有效的方法:

matrix = [shift(row) for row in matrix]
此外,如果我将矩阵向其他三个方向移动(除右外),是否有比这三个方向更有效的方法:

#Left
matrix = [shift(row[::-1])[::-1] for row in matrix]

#Down
matrix = map(list, zip(*[shift(row) for row in map(list, zip(*matrix))]))

#Up
matrix = map(list, zip(*[shift(row[::-1])[::-1] for row in map(list, zip(*matrix))]))
如果这些移位操作被重复执行(以及矩阵中的一个值每次都被更改),我是否应该跟踪哪些事情以提高效率

编辑

我的功能并不总是有效:

>>> shift([1, 1, 1, 1])
[0, 2, 0, 2]
输出应为:

[0, 0, 2, 2]
更多预期投入和产出:

[1, 1, 1]             --> [0, 1, 2]
[1, 2, 2, 3, 5, 5, 2] --> [0, 0, 1, 4, 3, 10, 2]
编辑2


它不必向右移动,也可以向右移动。

这是否更有效取决于
timeit

def streaming_sum(sequence):
    values = reversed(sequence)
    last = next(values)
    for value in values:
        if value == last:
            yield last + value
            last = 0
        else:
            yield last
            last = value
    yield last


def shift(sequence):
    length = len(sequence)
    reduced = list(reversed(filter(None, streaming_sum(sequence))))
    return [0] * (length - len(reduced)) + reduced


for sequence, expected in [
    ([0, 0, 1, 0], [0, 0, 0, 1]),
    ([1, 1, 0, 0], [0, 0, 0, 2]),
    ([2, 0, 1, 0], [0, 0, 2, 1]),
    ([1, 1, 1, 1], [0, 0, 2, 2]),
    ([1, 1, 1], [0, 1, 2]),
    ([1, 2, 2, 3, 5, 5, 2], [0, 0, 1, 4, 3, 10, 2]),
]:
    actual = shift(sequence)
    assert actual == expected, (actual, expected)

这是我目前的解决方案。它比Kirk Strauser的解决方案快约60%

def shift(self, sequence, right=False):
    if right:
        sequence = sequence[::-1]
    values = []
    empty = 0
    for n in sequence:
        if values and n == values[-1]:
            values[-1] = 2*n
            empty += 1
        elif n:
            values.append(n)
        else:
            empty += 1
    values += [0]*empty
    if right:
        values = values[::-1]
    return values
我的效率更高:

def shift2(length, sequence, right=False):
    if right:
        sequence = sequence[::-1]
    values = [0]*length
    full = 0
    for n in sequence:
        if full and n == values[full]:
            values[full] = 2*n
        elif n:
            values[full] = n
            full += 1
    if right:
        values = values[::-1]
    return values
柯克的解决方案:

def streaming_sum(sequence):
    values = reversed(sequence)
    last = next(values)
    for value in values:
        if value == last:
            yield last + value
            last = 0
        else:
            yield last
            last = value
    yield last

def shift2(sequence):
    length = len(sequence)
    reduced = list(reversed(list(filter(None, streaming_sum(sequence)))))
    return [0] * (length - len(reduced)) + reduced
我对柯克功能的改进(40%加速):

def shift3(sequence):
    length = len(sequence)
    reduced = [n for n in filter(None, streaming_sum(sequence))][::-1]
    return [0] * (length - len(reduced)) + reduced

时间:

from timeit import Timer

tests = [[1000, 1000, 1000, 1000],
         [1000, 0, 0, 1000],
         [0, 1000],
         [1000, 1000, 0, 500, 0, 500, 1000, 0, 0, 100, 100, 100]]
t1, t2, t3 = 0, 0, 0

for test in tests:
    t1 += Timer(lambda: shift(test)).timeit()
    t2 += Timer(lambda: shift2(test)).timeit()
    t3 += Timer(lambda: shift3(test)).timeit()

>>> print(t1, t2, t3)
10.706327316242147 26.92895738572211 16.65189852514444
我的一对我的效率更高的一对我的效率更高的一的计时,没有右对齐而不是左对齐的选项:

10.502816527107633 8.503653343656246 8.15101370397509

这像1024游戏吗?@Cyber我没听说过,所以没有。合并是递归的吗?或者换句话说,
[1,1,1,1]
的结果是什么?@NiklasB<代码>[1,1,1,1]-->[0,0,2,2]。实际上,这意味着我的函数不适用于此…你应该提到2048,因为每个了解游戏的人都可以更容易地理解这个问题。另外,您的方法已经是渐近最优的,它似乎会导致一个错误:
TypeError:type'filter'的对象没有len()
@Scorpion\u God这是因为
filter
在python2和python3中返回不同的对象(列表和生成器),这将使
[1,1]
->
[3]
。同意该问题未指定。这会导致另一个错误:
TypeError:argument to reversed()必须是一个序列
Ahh,您使用的是Python 3。将
filter()
包装在
list()
中。不过,我认为您的
shift
不正确。它从左到右工作,并在右侧添加零。例如,
shift([1,1,1,2,2])
=>
[2,1,4,0,0]
,但它应该是
[0,0,1,2,4]
。它对
序列也有副作用,这可能是不需要的。@TooTone如果你读了我的第二次编辑,我说它可能是另一种方式。
10.502816527107633 8.503653343656246 8.15101370397509