1

背景

我们有一个家庭传统,我和我的兄弟姐妹的圣诞礼物是用一个代码来识别的,这个代码只能使用与我们相关的数字来解决。例如,代码可以是出生月份*年龄+毕业年份(这是一个简单的)。如果数字是 8 * 22 + 2020 = 2196,那么 2196 将写在我所有的圣诞礼物上。

我已经创建了一个 Python 类来解决具有某些约束的代码,但我想知道是否可以递归地执行它。

当前代码

第一个函数为在 target_values 中产生值的所有可能的数字和操作组合返回一个结果集

#Master algorithm (Get the result set of all combinations of numbers and cartesian products of operations that reach a target_value, using only the number_of_numbers_in_solution)
#Example: sibling1.results[1] = [(3, 22, 4), (<built-in function add>, <built-in function add>), 29]. This means that 3 + 22 + 4 = 29, and 29 is in target_values
import operator
from itertools import product
from itertools import combinations

NUMBER_OF_OPERATIONS_IN_SOLUTION = 2 #Total numbers involved is this plus 1
NUMBER_OF_NUMBERS_IN_SOLUTION = NUMBER_OF_OPERATIONS_IN_SOLUTION + 1
TARGET_VALUES = {22,27,29,38,39}

def getresults( list ):
  #Add the cartesian product of all possible operations to a variable ops
  ops = []
  opslist = [operator.add, operator.sub, operator.mul, operator.truediv]
  for val in product(opslist, repeat=NUMBER_OF_OPERATIONS_IN_SOLUTION):
    ops.append(val)

  #Get the result set of all combinations of numbers and cartesian products of operations that reach a target_value
  results = []
  for x in combinations(list, NUMBER_OF_NUMBERS_IN_SOLUTION):
      for y in ops:
          result = 0
          for z in range(len(y)):
              #On the first iteration, do the operation on the first two numbers (x[z] and x[z+1])
              if (z == 0):
                  #print(y[z], x[z], x[z+1])
                  result = y[z](x[z], x[z+1])
              #For all other iterations, do the operation on the current result and x[z+1])
              else:
                  #print(y[z], result, x[z+1])
                  result = y[z](result, x[z+1])
          if result in TARGET_VALUES:
            results.append([x, y, result])
            #print (x, y)
  print(len(results))
  return results

然后是一个类,它接受每个人的个人参数并获取结果集

def getalpha( str, inverse ):
   "Converts string to alphanumeric array of chars"
   array = []
   for i in range(0, len(str)): 
      alpha = ord(str[i]) - 96
      if inverse:
        array.append(27 - alpha)
      else:
        array.append(alpha)
   return array;

class Person:
  def __init__(self, name, middlename, birthmonth, birthday, birthyear, age, orderofbirth, gradyear, state, zip, workzip, cityfirst3):
    #final list
    self.listofnums = []
    self.listofnums.extend((birthmonth, birthday, birthyear, birthyear - 1900, age, orderofbirth, gradyear, gradyear - 2000, zip, workzip))
    self.listofnums.extend(getalpha(cityfirst3, False))
    self.results = getresults(self.listofnums)

最后,一个“解决代码”方法从结果集中获取并找到产生完整目标值列表的任何可能组合。

#Compares the values of two sets
def compare(l1, l2):
    result = all(map(lambda x, y: x == y, l1, l2))
    return result and len(l1) == len(l2)

#Check every result in sibling2 with a different result target_value and equal operation sets
def comparetwosiblings(current_values, sibling1, sibling2, a, b):
  if sibling2.results[b][2] not in current_values and compare(sibling1.results[a][1], sibling2.results[b][1]):
    okay = True
    #If the indexes aren't alphanumeric, ensure they're the same before adding to new result set
    for c in range(0, NUMBER_OF_NUMBERS_IN_SOLUTION):
      indexintersection = set([index for index, value in enumerate(sibling1.listofnums) if value == sibling1.results[a][0][c]]) & set([index for index, value in enumerate(sibling2.listofnums) if value == sibling2.results[b][0][c]])
      if len(indexintersection) > 0:
        okay = True
      else:
        okay = False
        break
  else:
    okay = False
  return okay

#For every result, we start by adding the result number to the current_values list for sibling1, then cycle through each person and see if a matching operator list leads to a different result number. (Matching indices as well)
#If there's a result set for everyone that leads to five different numbers in the code, the values will be added to the newresult set
def solvecode( sibling1, sibling2, sibling3, sibling4, sibling5 ):
  newresults = []
  current_values = []

  #For every result in sibling1
  for a in range(len(sibling1.results)):
    current_values = []
    current_values.append(sibling1.results[a][2])
    for b in range(len(sibling2.results)):
      if comparetwosiblings(current_values, sibling1, sibling2, a, b):
        current_values.append(sibling2.results[b][2])
        for c in range(len(sibling3.results)):
          if comparetwosiblings(current_values, sibling1, sibling3, a, c):
            current_values.append(sibling3.results[c][2])
            for d in range(len(sibling4.results)):
              if comparetwosiblings(current_values, sibling1, sibling4, a, d):
                current_values.append(sibling4.results[d][2])
                for e in range(len(sibling5.results)):
                  if comparetwosiblings(current_values, sibling1, sibling5, a, e):
                    newresults.append([sibling1.results[a][0], sibling2.results[b][0], sibling3.results[c][0], sibling4.results[d][0], sibling5.results[e][0], sibling1.results[a][1]])
                current_values.remove(sibling4.results[d][2])
            current_values.remove(sibling3.results[c][2])
        current_values.remove(sibling2.results[b][2])

  print(len(newresults))
  print(newresults)

这是我想知道是否可以优化并制成递归算法的最后一个“solvecode”方法。在某些情况下,添加或删除一个兄弟姐妹可能会有所帮助,这会递归看起来不错(我妈妈有时会在一个兄弟姐妹上犯错,或者我们有一个新的兄弟/嫂子)

感谢您的任何帮助!我希望你至少能从我奇怪的家庭传统中得到一笑。

编辑:如果您想测试算法,这里有一组兄弟姐妹的示例,它们会产生一个正确的解决方案

#ALL PERSONAL INFO CHANGED FOR STACKOVERFLOW
sibling1 = Person("sibling1", "horatio", 7, 8, 1998, 22, 5, 2020, "ma", 11111, 11111, "red")
sibling2 = Person("sibling2", "liem", 2, 21, 1995, 25, 4, 2018, "ma", 11111, 11111, "pho")
sibling3 = Person("sibling3", "kyle", 4, 21, 1993, 26, 3, 2016, "ma", 11111, 11111, "okl")
sibling4 = Person("sibling4", "jamal", 4, 7, 1991, 29, 2, 2014, "ma", 11111, 11111, "pla")
sibling5 = Person("sibling5", "roberto", 9, 23, 1990, 30, 1, 2012, "ma", 11111, 11111, "boe")
4

1 回答 1

1

我只是花了一段时间改进代码。我需要提到的几件事:

  • 使用 python 关键字(如 list、str 和 zip)作为变量不是一个好习惯,它会给您带来问题,并且使调试变得更加困难。
  • 我觉得你应该使用排列函数,因为组合给出了无序对,而排列给出了数量更多并且会产生更多结果的有序对。例如,对于您提供的兄弟信息,组合通过 solvecode() 仅提供 1 个解决方案,而 permutation 提供 12 个解决方案。
  • 因为您正在使用运算符,所以可能会有更多使用括号的情况。为了解决这个问题并使 getresults() 函数更加优化,我建议您探索反向波兰符号。Computerphile 有一个很棒的视频。
  • 您不需要比较功能。list1==list2 有效。

这是优化后的代码:

import operator
from itertools import product
from itertools import permutations

NUMBER_OF_OPERATIONS_IN_SOLUTION = 2 #Total numbers involved is this plus 1
NUMBER_OF_NUMBERS_IN_SOLUTION = NUMBER_OF_OPERATIONS_IN_SOLUTION + 1
TARGET_VALUES = {22,27,29,38,39}

def getresults(listofnums):
    #Add the cartesian product of all possible operations to a variable ops
    ops = []
    opslist = [operator.add, operator.sub, operator.mul, operator.truediv]
    for val in product(opslist, repeat=NUMBER_OF_OPERATIONS_IN_SOLUTION):
        ops.append(val)

    #Get the result set of all combinations of numbers and cartesian products of operations that reach a target_value
    results = []
    for x in permutations(listofnums, NUMBER_OF_NUMBERS_IN_SOLUTION):
        for y in ops:
            result = y[0](x[0], x[1])
            if NUMBER_OF_OPERATIONS_IN_SOLUTION>1:
                for z in range(1, len(y)):
                    result = y[z](result, x[z+1])
            if result in TARGET_VALUES:
                results.append([x, y, result])
    return results

def getalpha(string, inverse):
    "Converts string to alphanumeric array of chars"
    array = []
    for i in range(0, len(string)): 
        alpha = ord(string[i]) - 96
        array.append(27-alpha if inverse else alpha)
    return array

class Person:
    def __init__(self, name, middlename, birthmonth, birthday, birthyear, age, orderofbirth, gradyear, state, zipcode, workzip, cityfirst3):
        #final list
        self.listofnums = [birthmonth, birthday, birthyear, birthyear - 1900, age, orderofbirth, gradyear, gradyear - 2000, zipcode, workzip]
        self.listofnums.extend(getalpha(cityfirst3, False))
        self.results = getresults(self.listofnums)

#Check every result in sibling2 with a different result target_value and equal operation sets
def comparetwosiblings(current_values, sibling1, sibling2, a, b):
    if sibling2.results[b][2] not in current_values and sibling1.results[a][1]==sibling2.results[b][1]:
        okay = True
        #If the indexes aren't alphanumeric, ensure they're the same before adding to new result set
        for c in range(0, NUMBER_OF_NUMBERS_IN_SOLUTION):
            indexintersection = set([index for index, value in enumerate(sibling1.listofnums) if value == sibling1.results[a][0][c]]) & set([index for index, value in enumerate(sibling2.listofnums) if value == sibling2.results[b][0][c]])
            if len(indexintersection) > 0:
                okay = True
            else:
                okay = False
                break
    else:
        okay = False
    return okay

现在,百万美元的功能,或者我应该说两个功能:

# var contains the loop variables a-e, depth keeps track of sibling number
def rec(arg, var, current_values, newresults, depth):
    for i in range(len(arg[depth].results)):
        if comparetwosiblings(current_values, arg[0], arg[depth], var[0], i):
            if depth<len(arg)-1:
                current_values.append(arg[depth].results[i][2])
                rec(arg, var[:depth]+[i], current_values, newresults, depth+1)
                current_values.remove(arg[depth].results[i][2])
            else:
                var.extend([i])
                newresults.append([arg[0].results[var[0]][0], arg[1].results[var[1]][0], arg[2].results[var[2]][0], arg[3].results[var[3]][0], arg[4].results[var[4]][0], arg[0].results[var[0]][1]])

def solvecode(*arg):
    newresults = []
    for a in range(len(arg[0].results)):
        current_values = [arg[0].results[a][2]]
        rec(arg, var=[a], current_values=current_values, newresults=newresults, depth=1)
    print(len(newresults))
    print(newresults)

需要两个函数,因为第一个是递归函数,第二个函数类似于包装。我还实现了您的第二个愿望,那就是能够将可变数量的兄弟数据输入到新的求解代码函数中。我检查了新函数,它们的工作方式与原来的求解代码函数完全一样。需要注意的是,虽然第二个版本少了 8 行代码,但该版本的运行时并没有显着差异。希望这有帮助。lmao花了我3个小时。

于 2020-12-25T03:59:15.600 回答