A* algorithm - Order of expanded nodes - algorithm

I am wondering why the order of expansion is ACBDG and not just ACG. From what i understand from the A*-algorithm is that is uses the f-value, which is the heuristic value and the real cost combined to determine the next node to expand. Is is that when expanding a node, the previous cost will be the f-value from the next node and current node combined? Or the f-value of the next node and g-value from current node combined?
It seems strange that after expanding the A, C and B nodes that the next node would not be G, as H(G) is lower than H(D). I observe that the real cost is lower to D, but isnt the point of A* to go for the combined heuristic value and the actual cost?
The starting node is A and the goal is G

In the following steps to the left of the => are the unexpanded nodes, and on the right are the action and the result of the expansion. Nodes on the left contain between the parenthesis the path, the cost of that path, and heuristic value. On the right there is no heuristic value yet. Nodes on the left are sorted by cost of path + heuristic value, with later nodes added after the existing nodes if they have equal sums:
A(-, 7) => Expand A from -: got B(A, 1), C(A, 4)
C(A, 4, 2), B(A, 1, 6) => Expand C from A: got B(AC, 6), D(AC,
7), G(AC, 8)
B(A, 1, 6), G(AC, 8, 0), D(AC, 7, 2), B(AC, 6, 6) =>
Expand B from A: got C(AB, 3), D(AB, 5)
C(AB, 3, 2), D(AB, 5, 2), G(AC, 8, 0), D(AC, 7, 2), B(AC, 6, 6) => Expand C from AB: got D(ABC, 6), G(ABC, 7)
D(AB, 5, 2), G(ABC, 7, 0), G(AC, 8, 0), D(ABC, 6, 2), D(AC, 7, 2), B(AC, 6, 6) => Expand D from AB: got C(ABD, 8), G(ABD, 11)
G(ABC, 7, 0), G(AC, 8, 0), D(ABC, 6, 2), D(AC, 7, 2), C(ABD, 8, 2), G(ABD, 11, 0), B(AC, 6, 6) => Found G from ABC, end of search

Related

Even distribution algorithm

I am organizing a tournament where 12 players are going to meet each other on 10 board games.
I want each player to play at least one time with the 11 others players over the 10 board games.
For example :
BoardGame1 - Match1 - Player1 + Player2 + Player3
BoardGame1 - Match2 - Player4 + Player5 + Player6
BoardGame1 - Match3 - Player7 + Player8 + Player9
BoardGame1 - Match4 - Player10 + Player11 + Player12
[...]
BoardGame10 - Match1 - Player1 + Player11 + Player9
BoardGame10 - Match2 - Player4 + Player2 + Player12
BoardGame10 - Match3 - Player7 + Player5 + Player3
BoardGame10 - Match4 - Player10 + Player8 + Player6
How do you create an algorithm where you distribute the players evenly?
I'd like to do it with TDD approach, so I need to predict the expected result (meaning no random distribution).
If all players play each other exactly once, then the resulting object
would be a Kirkman Triple System. There is no KTS with the parameters
you want, but since each player has twenty opponent slots and only
eleven potential opponents, it should be easy to find a suitable
schedule.
The code below generates the (11 choose 2) × (8 choose 2) × (5 choose 2)
= 15400 possibilities for one game and repeatedly greedily chooses the
one that spreads the pairings in the fairest manner.
import collections
import itertools
import pprint
def partitions(players, k):
players = set(players)
assert len(players) % k == 0
if len(players) == 0:
yield []
else:
players = players.copy()
leader = min(players)
players.remove(leader)
for comb in itertools.combinations(sorted(players), k - 1):
group = {leader} | set(comb)
for part in partitions(players - group, k):
yield [group] + part
def update_pair_counts(pair_counts, game):
for match in game:
pair_counts.update(itertools.combinations(sorted(match), 2))
def evaluate_game(pair_counts, game):
pair_counts = pair_counts.copy()
update_pair_counts(pair_counts, game)
objective = [0] * max(pair_counts.values())
for count in pair_counts.values():
objective[count - 1] += 1
total = 0
for i in range(len(objective) - 1, -1, -1):
total += objective[i]
objective[i] = total
return objective
def schedule(n_groups, n_players_per_group, n_games):
games = list(partitions(range(n_groups * n_players_per_group), n_players_per_group))
pair_counts = collections.Counter()
for i in range(n_games):
game = max(games, key=lambda game: evaluate_game(pair_counts, game))
yield game
update_pair_counts(pair_counts, game)
def main():
pair_counts = collections.Counter()
for game in schedule(4, 3, 10):
pprint.pprint(game)
update_pair_counts(pair_counts, game)
print()
pprint.pprint(pair_counts)
if __name__ == "__main__":
main()
Sample output:
[{0, 1, 2}, {3, 4, 5}, {8, 6, 7}, {9, 10, 11}]
[{0, 3, 6}, {1, 4, 9}, {2, 10, 7}, {8, 11, 5}]
[{0, 4, 7}, {3, 1, 11}, {8, 9, 2}, {10, 5, 6}]
[{0, 1, 5}, {2, 11, 6}, {9, 3, 7}, {8, 10, 4}]
[{0, 8, 3}, {1, 10, 6}, {2, 11, 4}, {9, 5, 7}]
[{0, 10, 11}, {8, 1, 7}, {2, 3, 5}, {9, 4, 6}]
[{0, 9, 2}, {1, 10, 3}, {8, 4, 5}, {11, 6, 7}]
[{0, 4, 6}, {1, 2, 5}, {10, 3, 7}, {8, 9, 11}]
[{0, 5, 7}, {1, 11, 4}, {8, 2, 10}, {9, 3, 6}]
[{0, 3, 11}, {8, 1, 6}, {2, 4, 7}, {9, 10, 5}]
Counter({(0, 3): 3,
(0, 1): 2,
(0, 2): 2,
(1, 2): 2,
(3, 5): 2,
(4, 5): 2,
(6, 7): 2,
(6, 8): 2,
(7, 8): 2,
(9, 10): 2,
(9, 11): 2,
(10, 11): 2,
(0, 6): 2,
(3, 6): 2,
(1, 4): 2,
(4, 9): 2,
(2, 7): 2,
(2, 10): 2,
(7, 10): 2,
(5, 8): 2,
(8, 11): 2,
(0, 4): 2,
(0, 7): 2,
(4, 7): 2,
(1, 3): 2,
(1, 11): 2,
(3, 11): 2,
(2, 8): 2,
(2, 9): 2,
(8, 9): 2,
(5, 10): 2,
(6, 10): 2,
(0, 5): 2,
(1, 5): 2,
(2, 11): 2,
(6, 11): 2,
(3, 7): 2,
(3, 9): 2,
(7, 9): 2,
(4, 8): 2,
(8, 10): 2,
(1, 6): 2,
(1, 10): 2,
(2, 4): 2,
(4, 11): 2,
(5, 7): 2,
(5, 9): 2,
(0, 11): 2,
(1, 8): 2,
(2, 5): 2,
(4, 6): 2,
(6, 9): 2,
(3, 10): 2,
(3, 4): 1,
(1, 9): 1,
(5, 11): 1,
(5, 6): 1,
(2, 6): 1,
(4, 10): 1,
(0, 8): 1,
(3, 8): 1,
(0, 10): 1,
(1, 7): 1,
(2, 3): 1,
(0, 9): 1,
(7, 11): 1})
How do you create an algorithm where you distribute the players evenly? I'd like to do it with TDD approach, so I need to predict the expected result (meaning no random distribution).
TDD tends to be successful when your problem is that you know what the computer should do, and you know how to make the computer do that, but you don't know the best way to write the code.
When you don't know how to make the computer do what you want, TDD is a lot harder. There are two typical approaches taken here.
The first is to perform a "spike" - sit down and hack things until you understand how to make the computer do what you want. The key feature of spikes is that you don't get to keep the code changes at the end; instead, you discard your spiked code, keep what you have learned in your head, and start over by writing the tests that you need.
The second approach is to sort of sneak up on it - you do TDD for the very simple cases that you do understand, and keep adding tests that are just a little bit harder than what you have already done. See Robert Martin's Craftsman series for an example of this approach.
For this problem, you might begin by first thinking of an interface that you might use for accessing the algorithm. For instance, you might consider a design that accepts as input a number of players and a number of games, and returns you a sequence of tuples, where each tuple represents a single match.
Typically, this version of the interface will look like general purpose data structures as inputs (in this example: numbers), and general purpose data structures as outputs (the list of tuples).
Most commonly, we'll verify the behavior in each test by figuring out what the answer should be for a given set of inputs, and asserting that the actual data structure exactly matches the expected. For a list of tuples, that would look something like:
assert len(expected) == len(actual)
for x in range(actual):
assert len(expected[x]) == len(actual[x])
for y in range(actual[x]):
assert expected[x][y] == actual[x][y]
Although of course you could refactor that into something that looks nicer
assert expected == actual
Another possibility is to think about the properties that a solution should have, and verify that the actual result is consistent with those properties. Here, you seem to have two properties that are required for every solution:
Each pair of players should appear exactly once in the list of matches
Every player, boardgame pair should appear exactly once in the list of matches
In this case, the answer is easy enough to check (iterate through all of the matches, count each pair, assert count equals one).
The test themselves we introduce by starting with the easiest example we can think of. Here, that might be the case where we have 2 players and 1 board, and our answer should be
BoardGame1 - Match1 - Player1 + Player2
And so we write that test (RED), and hard code this specific answer (GREEN), and then (REFACTOR) the code so that it is clear to the reader why this is the correct answer for these inputs.
And when you are happy with that code, you look for the next example - an example where the current implementation returns the wrong answer, but the change that you need to make to get it to return the write answer is small/easy.
Often, what will happen is that we "pass" the next test using a branch:
if special_case(inputs):
return answer_for_special_case
else:
# ... real implementation here ...
return answer_for_general_case
And then refactor the code until the two blocks are the same, then finally remove the if clause.
It will sometimes happen that the new test is too big, and we can't figure out how to extend the algorithm to cover the new case. Usually the play is to revert any changes we've made (keeping the tests passing), and use what we have learned to find a different test that might be easier to introduce to the code.
And you keep iterating on this process until you have solved "all" of the problem.
Here is a resolvable triple system due to Haim Hanani (“On resolvable
balanced incomplete block designs”, 1974), which provides a schedule for
11 games (drop one). Unfortunately it repeats matches.
import collections
import itertools
from pprint import pprint
def Match(a, b, c):
return tuple(sorted([a, b, c]))
games = []
for j in range(4):
games.append(
[
Match(0 ^ j, 4 ^ j, 8 ^ j),
Match(1 ^ j, 2 ^ j, 3 ^ j),
Match(5 ^ j, 6 ^ j, 7 ^ j),
Match(9 ^ j, 10 ^ j, 11 ^ j),
]
)
games.append([Match(1 ^ j, 6 ^ j, 11 ^ j) for j in range(4)])
games.append([Match(2 ^ j, 7 ^ j, 9 ^ j) for j in range(4)])
games.append([Match(3 ^ j, 5 ^ j, 10 ^ j) for j in range(4)])
for j in range(4):
games.append(
[
Match(0 ^ j, 4 ^ j, 8 ^ j),
Match(1 ^ j, 6 ^ j, 11 ^ j),
Match(2 ^ j, 7 ^ j, 9 ^ j),
Match(3 ^ j, 5 ^ j, 10 ^ j),
]
)
for game in games:
game.sort()
pprint(len(games))
pprint(games)
pair_counts = collections.Counter()
for game in games:
for triple in game:
pair_counts.update(itertools.combinations(sorted(triple), 2))
pprint(max(pair_counts.values()))
Output:
11
[[(0, 4, 8), (1, 2, 3), (5, 6, 7), (9, 10, 11)],
[(0, 2, 3), (1, 5, 9), (4, 6, 7), (8, 10, 11)],
[(0, 1, 3), (2, 6, 10), (4, 5, 7), (8, 9, 11)],
[(0, 1, 2), (3, 7, 11), (4, 5, 6), (8, 9, 10)],
[(0, 7, 10), (1, 6, 11), (2, 5, 8), (3, 4, 9)],
[(0, 5, 11), (1, 4, 10), (2, 7, 9), (3, 6, 8)],
[(0, 6, 9), (1, 7, 8), (2, 4, 11), (3, 5, 10)],
[(0, 4, 8), (1, 6, 11), (2, 7, 9), (3, 5, 10)],
[(0, 7, 10), (1, 5, 9), (2, 4, 11), (3, 6, 8)],
[(0, 5, 11), (1, 7, 8), (2, 6, 10), (3, 4, 9)],
[(0, 6, 9), (1, 4, 10), (2, 5, 8), (3, 7, 11)]]
2
Combinatorial optimization is another possibility. This one doesn’t
scale super well but can handle 12 players/10 games.
import collections
import itertools
from pprint import pprint
def partitions(V):
if not V:
yield []
return
a = min(V)
V.remove(a)
for b, c in itertools.combinations(sorted(V), 2):
for part in partitions(V - {b, c}):
yield [(a, b, c)] + part
parts = list(partitions(set(range(12))))
from ortools.sat.python import cp_model
model = cp_model.CpModel()
vars = [model.NewBoolVar("") for part in parts]
model.Add(sum(vars) == 10)
pairs = collections.defaultdict(list)
for part, var in zip(parts, vars):
for (a, b, c) in part:
pairs[(a, b)].append(var)
pairs[(a, c)].append(var)
pairs[(b, c)].append(var)
for clique in pairs.values():
total = sum(clique)
model.Add(1 <= total)
model.Add(total <= 2)
solver = cp_model.CpSolver()
status = solver.Solve(model)
print(solver.StatusName(status))
schedule = []
for part, var in zip(parts, vars):
if solver.Value(var):
schedule.append(part)
pprint(schedule)
Sample output:
OPTIMAL
[[(0, 1, 6), (2, 4, 9), (3, 8, 11), (5, 7, 10)],
[(0, 1, 10), (2, 3, 5), (4, 8, 9), (6, 7, 11)],
[(0, 2, 4), (1, 8, 10), (3, 7, 11), (5, 6, 9)],
[(0, 2, 8), (1, 4, 11), (3, 7, 9), (5, 6, 10)],
[(0, 3, 6), (1, 4, 7), (2, 5, 8), (9, 10, 11)],
[(0, 3, 8), (1, 5, 11), (2, 6, 9), (4, 7, 10)],
[(0, 4, 5), (1, 2, 7), (3, 6, 10), (8, 9, 11)],
[(0, 5, 11), (1, 3, 9), (2, 6, 7), (4, 8, 10)],
[(0, 7, 9), (1, 6, 8), (2, 10, 11), (3, 4, 5)],
[(0, 9, 10), (1, 2, 3), (4, 6, 11), (5, 7, 8)]]

Visit every Node of Graph in multitple "splited" paths

Visit every Node of Graph in even Parts
Question
Given an undirected Graph G having n nodes and m edges (with weights). How do I get x Paths, that visit in total all edges m from a constant starting node and return in the end, of that path, to that starting node. Nodes can be visited more than once.
How do I write an algorithm to find those paths, that have the smallest maximum weight count. Secondly the total weight count should be minimized, but the most important part is that the highest weight count of one Path is as small as possible. The path should start from a given starting node and return after all times to that starting node.
Is this problem NP-Complete? (I think that it is, but I'm not sure and like to know why?)
I would be very happy about any help.
Example
Given this Graph with starting point 0 and x = 4
G = [(0, 2, w=1), (0, 8, w=1), (0, 6, w=1), (0, 4, w=1), (3, 2, w=1), (2, 1 w=1),
(1, 8, w=1), (8, 7, w=1), (7, 6, w=1), (6, 5, w=1), (5, 4, w=1), (4, 3, w=1)]
the best paths would be
([0, 6, 5, 4, 0], [0, 6, 7, 8, 0],
[0, 8, 1, 2, 0], [0, 2, 3, 4, 0])
because every path has a total weight count of 4. There are no path combinations where maximum total is smaller ...

Coalesce intersecting sets into disjoint sets

I am looking for an algorithm to coalesce a list of sets, that may intersect, into a list of sets with no intersection.
For instance:
my_sets = set(1, 2, 3), set(5, 6), set(4, 5, 6), set(4, 7), set(3, 8), set(9)
Should yield:
my_coalesced_sets = set(1, 2, 3, 8), set(4, 5, 6, 7), set(9)
Ideally an algorithm O(n)...
At the request of Ruben, here is one of the many algorithms I tried that does not yield correct results:
fun main(){
val l = mutableListOf(setOf(1, 2, 3), setOf(5, 6), setOf(4, 5, 6), setOf(4, 7), setOf(3, 8), setOf(9))
while (true){
val removes = mutableListOf<Set<Int>>()
var current = l.removeFirst()
l.filter { current.intersect(it).isNotEmpty() }.forEach {
current = current union it
removes += it
}
l += current
if (removes.isEmpty()){
break
}
l.removeAll(removes)
}
print(l)
}
This is the purpose of the disjoint-set data structure. Simply add an edge ("Union") for each consecutive pair in each input set, and the result will be the coalesced sets. The running time is essentially linear (very slightly super-linear but that's only a theoretical difference).

Dynamic programming - board with multiplier

I got quite standard DP problem - board nxn with integers, all positive. I want to start somewhere in the first row, end somewhere in the last row and accumulate as much sum as possible. From field (i,j) I can go to fields (i+1, j-1), (i+1, j), (i+1, j+1).
That's quite standard DP problem. But we add one thing - there can be an asterisk on the field, instead of the number. If we meet the asterisk, then we got 0 points from it, but we increase multiplier by 1. All numbers we collect later during our traversal are multiplied by multiplier.
I can't find out how to solve this problem with that multiplier thing. I assume that's still a DP problem - but how to get the equations right for it?
Thanks for any help.
You can still use DP, but you have to keep track of two values: The "base" value, i.e. without any multipliers applied to it, and the "effective" value, i.e. with multipliers. You work your way backwards through the grid, starting in the previous-to-last row, get the three "adjacent" cells in the row after that (the possible "next" cells on the path), and just pick the one with the highest value.
If the current cell is a *, you get the cell where base + effective is maximal, otherwise you just get the one where the effective score is highest.
Here's an example implementation in Python. Note that instead of * I'm just using 0 for multipliers, and I'm looping the grid in order instead of in reverse, just because it's more convenient.
import random
size = 5
grid = [[random.randint(0, 5) for _ in range(size)] for _ in range(size)]
print(*grid, sep="\n")
# first value is base score, second is effective score (with multiplier)
solution = [[(x, x) for x in row] for row in grid]
for i in range(1, size):
for k in range(size):
# the 2 or 3 values in the previous line
prev_values = solution[i-1][max(0, k-1):k+2]
val = grid[i][k]
if val == 0:
# multiply
base, mult = max(prev_values, key=lambda t: t[0] + t[1])
solution[i][k] = (base, base + mult)
else:
# add
base, mult = max(prev_values, key=lambda t: t[1])
solution[i][k] = (val + base, val + mult)
print(*solution, sep="\n")
print(max(solution[-1], key=lambda t: t[1]))
Example: The random 5x5 grid, with 0 corresponding to *:
[4, 4, 1, 2, 1]
[2, 0, 3, 2, 0]
[5, 1, 3, 4, 5]
[0, 0, 2, 4, 1]
[1, 0, 5, 2, 0]
The final solution grid with base values and effective values:
[( 4, 4), ( 4, 4), ( 1, 1), ( 2, 2), ( 1, 1)]
[( 6, 6), ( 4, 8), ( 7, 7), ( 4, 4), ( 2, 4)]
[( 9, 13), ( 5, 9), ( 7, 11), (11, 11), ( 9, 9)]
[( 9, 22), ( 9, 22), ( 9, 13), (11, 15), (12, 12)]
[(10, 23), ( 9, 31), (14, 27), (13, 17), (11, 26)]
Thus, the best solution for this grid is 31 from (9, 31). Working backwards through the grid solution grid, this corresponds to the path 0-0-5-0-4, i.e. 3*5 + 4*4 = 31, as there are 2 * before the 5, and 3 * before the 4.

Counting number of days, given a collection of day ranges?

Say I have the following ranges, in some list:
{ (1, 4), (6, 8), (2, 5), (1, 3) }
(1, 4) represents days 1, 2, 3, 4. (6, 8) represents days 6, 7, 8, and so on.
The goal is to find the total number of days that are listed in the collection of ranges -- for instance, in the above example, the answer would be 8, because days 1, 2, 3, 4, 6, 7, 8, and 5 are contained within the ranges.
This problem can be solved trivially by iterating through the days in each range and putting them in a HashSet, then returning the size of the HashSet. But is there any way to do it in O(n) time with respect to the number of range pairs? How about in O(n) time and with constant space? Thanks.
Sort the ranges in ascending order by their lower limits. You can probably do this in linear time since you're dealing with integers.
The rest is easy. Loop through the ranges once keeping track of numDays (initialized to zero) and largestDay (initialized to -INF). On reaching each interval (a, b):
if b > largestDay then
numDays <- numDays + b-max(a - 1, largestDay)
largestDay <- max(largestDay, b)
else nothing.
So, after sorting we have (1,4), (1,3), (2,5), (6,8)
(1,4): numDays <- 0 + (4 - max(1 - 1, -INF)) = 4, largestDay <- max(-INF, 4) = 4
(1,3): b < largestDay, so no change.
(2,5): numDays <- 4 + (5 - max(2 - 1, 4)) = 5, largestDay <- 5
(6,8): numDays <- 5 + (8 - max(6-1, 5)) = 8, largestDay <- 8
The complexity of the following algorithm is O(n log n) where n is the number of ranges.
Sort the ranges (a, b) lexicographically by increasing a then by decreasing b.
Before: { (1, 4), (6, 8), (2, 5), (1, 3) }
After: { (1, 4), (1, 3), (2, 5), (6, 8) }
Collapse the sorted sequence of ranges into a potentially-shorter sequence of ranges, repeatedly merging consecutive (a, b) and (c, d) into (a, max(b, d)) if b >= c.
Before: { (1, 4), (1, 3), (2, 5), (6, 8) }
{ (1, 4), (2, 5), (6, 8) }
After: { (1, 5), (6, 8) }
Map the sequence of ranges to their sizes.
Before: { (1, 5), (6, 8) }
After: { 5, 3 }
Sum the sizes to arrive at the total number of days.
8

Resources