Python逻辑以在笛卡尔坐标系上找到两组点之间的最小总距离。

huangapple go评论66阅读模式
英文:

Python logic to find minimum total distance between 2 groups of points on a cartesian system

问题

Sure, here's the translated content you requested:

想象一个笛卡尔坐标系的游戏场地。它有3个不同位置的旗帜,也有3个不同位置的玩家。目标是让一名玩家到达旗帜的位置。我正在尝试分配哪个玩家应该去哪个旗帜,以最小化所有3名玩家行驶的总距离。我正在寻找一个好的Python逻辑来实现这一目标,并确定哪个玩家应该去哪个旗帜。

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

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

因此,需要考虑的约束条件是:

  1. 一个玩家只能分配给一个旗帜
  2. 旗帜只需要被任何一个玩家到达一次
  3. 选择使三名玩家总行驶距离最小的情景。因此,即使'x'更接近旗帜'C',也将'x'分配给旗帜'B'。

不需要按我所示的方式存储数据。如果有更容易获得所需输出的数据存储方法,请提出建议。

英文:

Imagine a cartesian playing field. it has 3 flags in 3 different locations. Also has 3 players in 3 different locations. The objective is to get a player to the location of the flag. I am trying to assign which player should go to which flag by minimizing the total distance travelled by all 3 players. I am looking for a good python logic to implement this and identify which player should go to which flag.

# This is the dictionary of distances between each player and each flag.
# (A,B,C) are the flag IDs
# (x,y,z) are the player IDs
# so, the distance between Flag A and Player x is 12 units and so on

dist_dict = {
    "A": [{"x": 12}, {"y": 10}, {"z": 20}],
    "B": [{"x": 14}, {"y": 18}, {"z": 25}],
    "C": [{"x": 8}, {"y": 15}, {"z": 16}],
}

In this case the output expected is

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

So, constraints to be considered are:

  1. One player can only be assigned to one flag
  2. The flag only needs to be reached once by any player
  3. Select the scenario where the total distance traveled by all three players is minimum. hence, above player 'x' is assigned to flag 'B' even though 'x' is closer to flag 'C'

The data does not need to be stored in the way I have shown here. Please recommend a different way to storing that distance data if that makes it easier to get the desired output

答案1

得分: 1

内部数据结构(在dist_dict内部)也可以是一个字典,这样您可以通过它们的键访问所有距离值。

由于您只有三名玩家,可以遍历所有可能的将玩家分配到旗帜的方式,并查看哪种方式导致总距离最短。有一个itertools函数可以获得这个可迭代对象:permutations

英文:

The inner data structure (inside the dist_dict) could be a dictionary as well, so that you can access all the distance values by their keys.

Since you have only three players, it's feasible to iterate over all possible assignments of players to flags and see which one results in the lowest total distance. There is an itertools function to obtain this iterable: permutations.

from itertools import permutations

dist_dict = {'A': {'x': 12, 'y': 10, 'z': 20},
             'B': {'x': 14, 'y': 18, 'z': 25},
             'C': {'x': 8, 'y': 15, 'z': 16}}

flags = list(dist_dict)
players = list(dist_dict['A'])

min_total_dist = 1000

for player_order in permutations(players):
    total_dist = sum(dist_dict[flag][player] 
                     for flag, player in zip(flags, player_order))
    if total_dist < min_total_dist:
        min_total_dist = total_dist
        best_player_order = player_order

print("optimal solution:")
for player, flag in zip(best_player_order, flags):
    print(f"    player {player} to flag {flag}")

print(f"optimal total distance: {min_total_dist}")
optimal solution:
    player y to flag A
    player x to flag B
    player z to flag C
optimal total distance: 40

答案2

得分: 0

有不同的解决方法。以下是一个不太短的答案和解释:

from collections import defaultdict
from itertools import product

dist_dict = {
    "A": [{"x": 12}, {"y": 10}, {"z": 20}],
    "B": [{"x": 14}, {"y": 18}, {"z": 25}],
    "C": [{"x": 8}, {"y": 15}, {"z": 16}],
}

def get_indexes(t: tuple):
    """返回一个包含`t`中项在`player_moves`中索引的列表"""
    return [lst.index(i) for lst, i in zip(player_moves.values(), t)]

player_moves = defaultdict(list)
for lst in dist_dict.values():
    for d in lst:
        player, distance = d.copy().popitem()
        player_moves[player].append(distance)

minimun = float("inf")
moves = None
moves_indexes = None
for i in product(*player_moves.values()):
    indexes = get_indexes(i)
    if len(indexes) == len(set(indexes)):
        sum_of_distances = sum(i)
        if sum_of_distances < minimun:
            minimun = sum_of_distances
            moves = i
            moves_indexes = indexes

print(moves)
print(moves_indexes)
print({k: v[i].popitem()[0] for (k, v), i in zip(dist_dict.items(), moves_indexes)})

输出:

defaultdict(<class 'list'>, {'x': [12, 14, 8], 'y': [10, 18, 15], 'z': [20, 25, 16]})
(14, 10, 16)
[1, 0, 2]
{'A': 'y', 'B': 'x', 'C': 'z'}
英文:

There are different ways to solve this. Here is a not-too-short answer with explanation:

from collections import defaultdict
from itertools import product

dist_dict = {
    &quot;A&quot;: [{&quot;x&quot;: 12}, {&quot;y&quot;: 10}, {&quot;z&quot;: 20}],
    &quot;B&quot;: [{&quot;x&quot;: 14}, {&quot;y&quot;: 18}, {&quot;z&quot;: 25}],
    &quot;C&quot;: [{&quot;x&quot;: 8}, {&quot;y&quot;: 15}, {&quot;z&quot;: 16}],
}


def get_indexes(t: tuple):
    &quot;&quot;&quot;return a list containing the indexes of items of the `t` inside `player_moves&quot;&quot;&quot;
    return [lst.index(i) for lst, i in zip(player_moves.values(), t)]


# Creating a dictionary which maps player names to all available moves.
player_moves = defaultdict(list)
for lst in dist_dict.values():
    for d in lst:
        # Here a copy is needed because later we want to use this dictionary
        player, distance = d.copy().popitem()
        player_moves[player].append(distance)
print(player_moves)


# While iterating, we need to keep track of three things:
# 1. `minimun`: minimum sum of distances
# 2. `moves`: the instances that each player should go
# 3. `moves_indexes`: the indexes of numer 2.
minimun = float(&quot;inf&quot;)
moves = None
moves_indexes = None
# `product` generates all available moves for us.
for i in product(*player_moves.values()):
    indexes = get_indexes(i)

    # This check is needed because there shouldn&#39;t be a player with no move!
    if len(indexes) == len(set(indexes)):
        sum_of_distances = sum(i)
        if sum_of_distances &lt; minimun:
            minimun = sum_of_distances
            moves = i
            moves_indexes = indexes

print(moves)
print(moves_indexes)
print({k: v[i].popitem()[0] for (k, v), i in zip(dist_dict.items(), moves_indexes)})

output:

defaultdict(&lt;class &#39;list&#39;&gt;, {&#39;x&#39;: [12, 14, 8], &#39;y&#39;: [10, 18, 15], &#39;z&#39;: [20, 25, 16]})
(14, 10, 16)
[1, 0, 2]
{&#39;A&#39;: &#39;y&#39;, &#39;B&#39;: &#39;x&#39;, &#39;C&#39;: &#39;z&#39;}

答案3

得分: 0

使用 itertools.permutations,并通过计算总距离的函数进行最小化。(可以对玩家或旗帜进行排列。在这种情况下,我对旗帜进行排列。)

import itertools

dist = {
    "A": {"x": 12, "y": 10, "z": 20},
    "B": {"x": 14, "y": 18, "z": 25},
    "C": {"x": 8, "y": 15, "z": 16},
}

def total_distance(perm):
    return sum(d[flag] for d, flag in zip(dist.values(), perm))

best_perm = min(itertools.permutations("xyz"), key=total_distance)
result = dict(zip(dist, best_perm))

另一种选择使用 pandasnumpy,更复杂但在有大量旗帜和玩家时可能更快。

import pandas as pd
import numpy as np
import itertools

dist = pd.DataFrame({
    "A": {"x": 12, "y": 10, "z": 20},
    "B": {"x": 14, "y": 18, "z": 25},
    "C": {"x": 8, "y": 15, "z": 16},
})

index = list(range(len(dist)))
perms = list(itertools.permutations(index))
best_perm_index = dist.to_numpy()[perms, index].sum(axis=1).argmin()
best_perm = list(perms[best_perm_index])
result = dict(zip(dist.columns, dist.iloc[best_perm].index))
英文:

Use itertools.permutations, and minimize using a function that calculates the total distance. (You can take permutations either of the players, or of the flags. In this case I permute the flags.)

import itertools

dist = { # I have altered the format of this input slightly
        &quot;A&quot;: {&quot;x&quot;: 12, &quot;y&quot;: 10, &quot;z&quot;: 20},
        &quot;B&quot;: {&quot;x&quot;: 14, &quot;y&quot;: 18, &quot;z&quot;: 25},
        &quot;C&quot;: {&quot;x&quot;: 8, &quot;y&quot;: 15, &quot;z&quot;: 16},
    }

def total_distance(perm):
    return sum(d[flag] for d, flag in zip(dist.values(), perm))

best_perm = min(itertools.permutations(&quot;xyz&quot;), key=total_distance)
result = dict(zip(dist, best_perm))

Another option usespandas and numpy, which is more complicated but might work faster if you have a large number of flags and players.

import pandas as pd
import numpy as np
import itertools
dist = pd.DataFrame({ 
    &quot;A&quot;: {&quot;x&quot;: 12, &quot;y&quot;: 10, &quot;z&quot;: 20},
    &quot;B&quot;: {&quot;x&quot;: 14, &quot;y&quot;: 18, &quot;z&quot;: 25},
    &quot;C&quot;: {&quot;x&quot;: 8, &quot;y&quot;: 15, &quot;z&quot;: 16},
})
# make a numeric index [0,1,2]
index = list(range(len(dist)))             

# get all permutations of [0,1,2]
perms = list(itertools.permutations(index))   

# find the index of the permutation of [0,1,2] that minimizes total distance
best_perm_index = dist.to_numpy()[perms, index].sum(axis=1).argmin()  

# get the permutation itself, as a list
best_perm = list(perms[best_perm_index])     

# convert back to a dictionary
result = dict(zip(dist.columns, dist.iloc[best_perm].index))  

huangapple
  • 本文由 发表于 2023年4月17日 20:35:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/76035218.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定