I have a coding problem that is very hard to explain, so I came up with this fruit bag swapping problem that is very easy to understand:
A smart fruiterer sells 3 kinds of fruits: apples (denoted as 'A'), bananas (denoted as 'B') and coconuts (denoted as 'C'). The weird thing about him is that he always prepares 10 bags (bag id 0 to 9), including 3 small bags that can contain 2 fruits, 3 medium bags that can contain 4 fruits, 3 large bags that can contain 8 fruits, and one extra large bag that can contain 16 fruits. He only puts in same type of fruit into each bag. This can be abstracted to the following python code:
import random
from string import ascii_uppercase
#length = 30
#max_size = 10
#bag_sizes = [random.randint(1, max_size) for _ in range(length)]
n_fruit = 3
bag_sizes = [2, 2, 2, 4, 4, 4, 8, 8, 8, 16]
random.seed(55)
fruiterer_bags = [[random.choice(ascii_uppercase[:n_fruit])] * bag_sizes[i]
for i in range(len(bag_sizes))]
print(fruiterer_bags)
Output:
[['A', 'A'], ['A', 'A'], ['A', 'A'],
['C', 'C', 'C', 'C'], ['B', 'B', 'B', 'B'], ['A', 'A', 'A', 'A'],
['C', 'C', 'C', 'C', 'C', 'C', 'C', 'C'],
['A', 'A', 'A', 'A', 'A', 'A', 'A', 'A'],
['B', 'B', 'B', 'B', 'B', 'B', 'B', 'B'],
['A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A']]
Note that every day the fruiterer put fruits in each bag like this.
The supplier comes to the fruit shop every day with the same 10 bags, including 3 small, 3 medium, 3 large, 1 extra large. The bags are also assigned with id 0 to 9. He also only puts in same type of fruit in each bag, but the fruits he puts in each bag are not exactly the same as the fruiterer's. Every day he comes with the following bags:
random.seed(100)
supplier_bags = [[random.choice(ascii_uppercase[:n_fruit])] * bag_sizes[i]
for i in range(len(bag_sizes))]
print(supplier_bags)
Output:
[['A', 'A'], ['B', 'B'], ['B', 'B'],
['A', 'A', 'A', 'A'], ['C', 'C', 'C', 'C'], ['B', 'B', 'B', 'B'],
['C', 'C', 'C', 'C', 'C', 'C', 'C', 'C'],
['B', 'B', 'B', 'B', 'B', 'B', 'B', 'B'],
['B', 'B', 'B', 'B', 'B', 'B', 'B', 'B'],
['C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C']]
The smart thing about this fruiterer is that every time the supplier comes, he wants to swap some fruit bags because they are fresher. The supplier says "OK, fine. You can swap however you like, you can do as many swaps as you want, but:
- I don't swap the bags with the same type of fruits in them;
- I must have the same amount of each fruit after all the swaps;
- I only swap whole bags, not individual fruit;
- I only swap bags with same ids;
- Each bag can only be swapped once;
- PLEASE BE FAST!"
Since supplier_bags has to have the same amount of each fruit after the swaps, then fruiterer_bags will of course also have the same amount of each fruit after the swaps. In this way, both fruiterer and supplier do not change the amount of each fruit on paper, but the smart fruiterer always gets fresher fruits. Every day the fruiterer and supplier come with same bags of fruits, but hopefully they can find very different ways of swapping bags (radomness).
My question is: is there any algorithm to get one possible solution very fast. The expected output is the indices of the bags being swapped. For example, one solution could be
swap_ids = [1, 2, 3, 4]
and the supplier_bags after the swaps becomes:
new_supplier_bags = supplier_bags.copy()
for i in swap_ids:
new_supplier_bags[i] = fruiterer_bags[i]
print(new_supplier_bags)
Output:
[['A', 'A'], ['A', 'A'], ['A', 'A'],
['C', 'C', 'C', 'C'], ['B', 'B', 'B', 'B'], ['B', 'B', 'B', 'B'],
['C', 'C', 'C', 'C', 'C', 'C', 'C', 'C'],
['B', 'B', 'B', 'B', 'B', 'B', 'B', 'B'],
['B', 'B', 'B', 'B', 'B', 'B', 'B', 'B'],
['C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C']]
which still has the same amount of each fruit as before swaps. This can be checked by
assert sorted([f for bag in new_supplier_bags for f in bag]) == sorted(
[f for bag in supplier_bags for f in bag])
Although I don't care about fruiterer_bagsat all, it must also have the same amount of each fruit after the swaps.
I don't want all possible solutions. I just want one fast solution with randomness, because in reality I am dealing with lists of more than 200 "bag"s with various sizes and many types of "fruit"s, which is infeasible for an enumeration. I want an algorithm to work for any given n_fruit, bag_sizes, fruiterer_bags and supplier_bags. I guess a greedy algorithm will do.
Also, Is there any way to quick check if there is a solution at all? If not, maybe we can say there is no solution after the greedy algorithm suggests e.g. 1000 wrong solutions (if fast enough).
Any suggestions?
from Fruit bag swapping problem: fast python algorithm to swap packed items between two lists without changing item counts
No comments:
Post a Comment