Python逻辑,用于查找笛卡尔系统上两组点之间的最小总距离

mqxuamgl  于 2023-04-19  发布在  Python
关注(0)|答案(3)|浏览(170)

想象一个笛卡尔的游戏场。它有3个旗帜在3个不同的位置。也有3个球员在3个不同的位置。目标是让一个球员的位置的旗帜。我试图分配哪个球员应该去哪个旗帜通过最小化的总距离由所有3个球员。我正在寻找一个好的Python逻辑来实现这一点,并确定哪个球员应该去哪个旗帜。

  1. # This is the dictionary of distances between each player and each flag.
  2. # (A,B,C) are the flag IDs
  3. # (x,y,z) are the player IDs
  4. # so, the distance between Flag A and Player x is 12 units and so on
  5. dist_dict = {
  6. "A": [{"x": 12}, {"y": 10}, {"z": 20}],
  7. "B": [{"x": 14}, {"y": 18}, {"z": 25}],
  8. "C": [{"x": 8}, {"y": 15}, {"z": 16}],
  9. }

在这种情况下,预期的输出为

  1. [{'A':'y'},{'B':'x'},{'C':'z'}]

因此,要考虑的约束条件是:
1.一名球员只能被分配到一面旗帜
1.任何玩家只需要到达旗帜一次
1.选择所有三个玩家行进的总距离最小的场景。因此,上面的玩家'x'被分配到旗'B',即使'x'更接近旗'C'
数据不需要以我在这里展示的方式存储。如果存储距离数据更容易获得所需的输出,请推荐一种不同的方法

y53ybaqx

y53ybaqx1#

内部数据结构(dist_dict内部)也可以是一个字典,这样您就可以通过键访问所有的距离值。
因为你只有三个玩家,所以可以迭代所有可能的玩家到旗帜的分配,看看哪一个会导致最小的总距离。有一个itertools函数可以获得这个可迭代对象:permutations

  1. from itertools import permutations
  2. dist_dict = {'A': {'x': 12, 'y': 10, 'z': 20},
  3. 'B': {'x': 14, 'y': 18, 'z': 25},
  4. 'C': {'x': 8, 'y': 15, 'z': 16}}
  5. flags = list(dist_dict)
  6. players = list(dist_dict['A'])
  7. min_total_dist = 1000
  8. for player_order in permutations(players):
  9. total_dist = sum(dist_dict[flag][player]
  10. for flag, player in zip(flags, player_order))
  11. if total_dist < min_total_dist:
  12. min_total_dist = total_dist
  13. best_player_order = player_order
  14. print("optimal solution:")
  15. for player, flag in zip(best_player_order, flags):
  16. print(f" player {player} to flag {flag}")
  17. print(f"optimal total distance: {min_total_dist}")
  1. optimal solution:
  2. player y to flag A
  3. player x to flag B
  4. player z to flag C
  5. optimal total distance: 40
展开查看全部
ux6nzvsh

ux6nzvsh2#

有不同的方法来解决这个问题。这里有一个不太简短的答案和解释:

  1. from collections import defaultdict
  2. from itertools import product
  3. dist_dict = {
  4. "A": [{"x": 12}, {"y": 10}, {"z": 20}],
  5. "B": [{"x": 14}, {"y": 18}, {"z": 25}],
  6. "C": [{"x": 8}, {"y": 15}, {"z": 16}],
  7. }
  8. def get_indexes(t: tuple):
  9. """return a list containing the indexes of items of the `t` inside `player_moves"""
  10. return [lst.index(i) for lst, i in zip(player_moves.values(), t)]
  11. # Creating a dictionary which maps player names to all available moves.
  12. player_moves = defaultdict(list)
  13. for lst in dist_dict.values():
  14. for d in lst:
  15. # Here a copy is needed because later we want to use this dictionary
  16. player, distance = d.copy().popitem()
  17. player_moves[player].append(distance)
  18. print(player_moves)
  19. # While iterating, we need to keep track of three things:
  20. # 1. `minimun`: minimum sum of distances
  21. # 2. `moves`: the instances that each player should go
  22. # 3. `moves_indexes`: the indexes of numer 2.
  23. minimun = float("inf")
  24. moves = None
  25. moves_indexes = None
  26. # `product` generates all available moves for us.
  27. for i in product(*player_moves.values()):
  28. indexes = get_indexes(i)
  29. # This check is needed because there shouldn't be a player with no move!
  30. if len(indexes) == len(set(indexes)):
  31. sum_of_distances = sum(i)
  32. if sum_of_distances < minimun:
  33. minimun = sum_of_distances
  34. moves = i
  35. moves_indexes = indexes
  36. print(moves)
  37. print(moves_indexes)
  38. print({k: v[i].popitem()[0] for (k, v), i in zip(dist_dict.items(), moves_indexes)})

输出:

  1. defaultdict(<class 'list'>, {'x': [12, 14, 8], 'y': [10, 18, 15], 'z': [20, 25, 16]})
  2. (14, 10, 16)
  3. [1, 0, 2]
  4. {'A': 'y', 'B': 'x', 'C': 'z'}
展开查看全部
iyr7buue

iyr7buue3#

使用itertools.permutations,并使用一个计算总距离的函数最小化。(您可以对球员或旗帜进行排列。在本例中,我对旗帜进行排列。)

  1. import itertools
  2. dist = { # I have altered the format of this input slightly
  3. "A": {"x": 12, "y": 10, "z": 20},
  4. "B": {"x": 14, "y": 18, "z": 25},
  5. "C": {"x": 8, "y": 15, "z": 16},
  6. }
  7. def total_distance(perm):
  8. return sum(d[flag] for d, flag in zip(dist.values(), perm))
  9. best_perm = min(itertools.permutations("xyz"), key=total_distance)
  10. result = dict(zip(dist, best_perm))

另一个选项使用pandasnumpy,这更复杂,但如果你有大量的旗帜和球员,可能会工作得更快。

  1. import pandas as pd
  2. import numpy as np
  3. import itertools
  4. dist = pd.DataFrame({
  5. "A": {"x": 12, "y": 10, "z": 20},
  6. "B": {"x": 14, "y": 18, "z": 25},
  7. "C": {"x": 8, "y": 15, "z": 16},
  8. })
  9. # make a numeric index [0,1,2]
  10. index = list(range(len(dist)))
  11. # get all permutations of [0,1,2]
  12. perms = list(itertools.permutations(index))
  13. # find the index of the permutation of [0,1,2] that minimizes total distance
  14. best_perm_index = dist.to_numpy()[perms, index].sum(axis=1).argmin()
  15. # get the permutation itself, as a list
  16. best_perm = list(perms[best_perm_index])
  17. # convert back to a dictionary
  18. result = dict(zip(dist.columns, dist.iloc[best_perm].index))
展开查看全部

相关问题