Python按钮算法

标签 python algorithm

我目前正在尝试使用 Python 以编程方式解决一个难题,我希望能够自己解决它,但我发现很难描述问题,因此我可以通过在线资源寻求帮助。下面我将描述问题的本质,非常感谢所提供的任何帮助。

因此,有一组 4 个彩色按钮,每个按钮都分配有一个功能,该功能以循环方式更改一个或多个按钮的颜色。这些按钮的代码表示可能如下:

# 1 = green, 2 = red, 3 = blue, 4 = purple
# goes 1->4 then loops
cstate = [1,3,4,1] # current state of the numbers

按钮可以执行的四种不同功能是:

  1. 自增 1
  2. 自身和对方都加 1
  3. 将自身和另外 2 个变量增加 1
  4. 全部加 1

每个按钮的每个功能都是唯一的,因此不能为两个按钮分配相同的功能。

我尝试表示这些函数是创建一个数组,描述通过单击每个按钮而受影响的按钮的索引,例如:

incArray =[[0,3],[0,1,3],[0,1,2,3],[3]]

接下来,我创建了一个函数,将按钮函数应用于上述 cstate 数组:

def increment(currentState, whichClicked, whichEffects):
    
    retArr = currentState
    for click in whichClicked:
        for change in whichEffects[click]:
            if currentState[change] == 4:
                retArr[change] = 1
            else:
                retArr[change] += 1
        print(retArr)

    return retArr

现在,在这个特定的示例中,我使用 whichClicked = [2,2,2,1,0,3,3] 提供了 increment 函数,据我所知正确的组合(或最终状态)为 fstate = [2,3,3,4]

我想要实现的是编写代码来生成上述的 whichClicked 数组,给定 cstatefstate。预先感谢您提供的任何帮助!

最佳答案

我倾向于从“愚蠢”的强力算法开始开发此类算法,然后进一步优化它

暴力破解

您可以通过某种 Breadth-first search algorithm 以“暴力”方式实现此目的,您将要:

  • 点击初始状态下的所有按钮(4个选项)
  • 对于所有结果状态,您将再次点击所有按钮(16 个选项)
  • 等等。您不断检查是否达到目标状态。

类似这样的事情:

from collections import deque
from dataclasses import dataclass

start_state = [1,3,4,1] # current state of the numbers
incArray =[[0,3],[0,1,3],[0,1,2,3],[3]]

@dataclass
class Node:
    path: list
    state: list

def apply_button(state, inc_array, button):
    new_state = state.copy()
    for affected_button in inc_array[button]:
        new_state[affected_button] = new_state[affected_button] % 4 + 1
    return new_state

def brute_force(start_state, inc_array, goal_state):
    iterations=0
    leafNodes = deque([Node([], start_state)])
    while True:
        node = leafNodes.popleft()
        for button in range(4):
            iterations+=1
            new_state = apply_button(node.state, inc_array, button)
            new_path = node.path + [button]
            if new_state==goal_state:
                print(f"iterations: {iterations}")
                return new_path
            leafNodes.append(Node(new_path, new_state))

print(brute_force(start_state,incArray,[2, 3, 3, 4]))
# OUTPUT:
# iterations: 7172
# [0, 1, 2, 2, 2, 3, 3]


第一次优化

您将看到结果输出与您在示例中提供的“whichClicked”数组相同,但所有项目均已排序。这是因为单击按钮的顺序不会影响最终结果。 您可以使用这些知识来优化您的算法,因为它正在评估大量冗余选项。 (例如路径 [0,1] 给出与路径 [1,0] 相同的结果)

因此,新策略可能是在解决方案中排除这些冗余选项。如果您在纸上绘制整个搜索图(或取消注释 # print(new_path) 行),您会看到以下代码仅迭代“排序”路径:


def brute_force_opt(start_state, inc_array, goal_state):
    iterations=0
    leafNodes = deque([Node([], start_state)])
    while True:
        node = leafNodes.popleft()
        min_button = node.path[-1] if len(node.path) else 0
        for button in range(min_button, 4):
            iterations+=1
            new_state = apply_button(node.state, inc_array, button)
            new_path = node.path + [button]
            # print(new_path)
            if new_state==goal_state:
                print(f"iterations: {iterations}")
                return new_path
            leafNodes.append(Node(new_path, new_state))

print(brute_force_opt(start_state,incArray,[2, 3, 3, 4]))
# OUTPUT:
# iterations: 283
# [0, 1, 2, 2, 2, 3, 3]

从输入中可以看出,迭代次数已从 7172 次减少到 283 次

现在要评估的第一条路径是:

[0]
[1]
[2]
[3]
[0, 0]
[0, 1]
[0, 2]
[0, 3]
[1, 1]
[1, 2]
[1, 3]
[2, 2]
[2, 3]
[3, 3]
[0, 0, 0]
[0, 0, 1]
[0, 0, 2]
[0, 0, 3]

已编辑

第二次优化

第二个优化可能是考虑到存在“循环”路径:例如按第四个按钮四次(路径[3,3,3,3])后,您将处于相同的状态。考虑到这一点的一种直接方法是保留您已经遇到的状态列表。如果您再次处于这种状态,您可以忽略它,因为它不会提供更好的解决方案(通过此循环路径到达解决方案的路径总是会更长):

def brute_force_opt2(start_state, inc_array, goal_state):
    iterations=0
    encoutered_states = set()
    leafNodes = deque([Node([], start_state)])
    while True:
        node = leafNodes.popleft()
        min_button = node.path[-1] if len(node.path) else 0
        for button in range(min_button, 4):
            new_state = apply_button(node.state, inc_array, button)
            if tuple(new_state) not in encoutered_states:
                iterations+=1
                new_path = node.path + [button]
                # print(new_path)
                if new_state==goal_state:
                    print(f"iterations: {iterations}")
                    return new_path
                leafNodes.append(Node(new_path, new_state))
                encoutered_states.add(tuple(new_state))

print(brute_force_opt2(start_state,incArray,[2, 3, 3, 4]))
# OUTPUT:
# iterations: 213
# [0, 1, 2, 2, 2, 3, 3]

如您所见,迭代次数现在仅为 182。正如预期的那样,该数字低于唯一状态的最大数量 (4^4 = 256)。

分析方法

假设这个问题的复杂性会更大(例如更多的按钮和颜色),暴力方法可能不可行,您可以考虑采用更具分析性的方法,例如计算每个按钮必须递增多少次(模 4)才能从开始状态到达结束状态,并找到满足所有按钮的要求的按钮单击组合。

关于Python按钮算法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65025999/

相关文章:

algorithm - 在不到 O(n^2) 的时间内模拟许多物体之间的重力

algorithm - 为什么我们在讨论时间复杂度时使用渐近符号(从而忽略系数)?

python - 如何在 "IN"运算符 Python Cassandra 驱动程序中使用 python 列表?

python - 如何在 pandas df 中设置新索引和删除默认索引

python - 我如何递归地遍历 2 个字典,并根据另一个字典修改原始字典?

algorithm - K-means聚类解的唯一性

javascript - 给定一个整数数组返回正数,其中存在等效的负数

Python2.7 subprocess32 : how to share environment between two scripts executed via Popen?

python - SimpleCV 点击图片后没有反应

Python 数字序列