[ 42, 45, 47, x, x] -> stop1 to stop2
[ 45, 47, 42, 88, x] -> stop2 to stop3
[ 21, 77, 42, x, x] -> stop3 to stop4
[ 22, 47, 42, 88, x] -> stop4 to stop5
[ 23, 47, 42, x, x] -> stop5 to stop6
[ 24, 47, 42, 8, 91] -> stop6 to stop7
[ 25, 13, 42, 3, 84] -> stop7 to stop8
[ 26, 10, 11, 4, 54] -> stop8 to stop9
[ 27, 9, 8, 88, 71] -> stop9 to stop10
x is there just for formatting. The first row means that there are only three buses from stop1 to stop2(42, 45, 47).
I have this matrix like structure where each row represents the buses going from one stop to another. I need to minimize the number of bus changes a person has to make to go from stop1 to stop10.
For example one of the output should be 42, 42, 42, 42, 42, 42, 42, 26, 27 another can be 42, 42, 42, 42, 42, 42, 42, 10, 9. If the number of changes is more than three I can discard the result.
What's the most optimal way to achieve this as brute forcing through it is pretty unefficient right now?
You can solve this problem by modeling it as a graph search.
Imagine you're a person and you're trying to get from point A to point B. The information most relevant to you is
where you currently are, and
which bus line, if any, you are currently on.
You can therefore model a person's state as a pair of a location (a bus stop) and a bus line (which might be "not on a line" when they start or finish). So create a graph with one node for each combination of a location and a bus line.
The edges in this graph will correspond to changes in state. You can change state either by
staying on your current bus line and going somewhere, or
switching bus lines.
If you're currently on a bus line, you can stay on that line to move from one location to the next if the line goes from the first location to the second. So create edges ((location1, line), (location2, line)) if bus line line goes from location1 to location2. This doesn't involve a transfer, so give this edge a cost of 0.
Alternatively, you can always get off of a bus or go from being off a bus to being on a bus. So add an edge ((location, line), (location, free)) for each line and each location (you always have the option to get off of a bus line) and give it cost 0, since this doesn't involve changing lines. Similarly, add edges ((location, free), (location, line)) for each bus line line available at the given location. Give it cost 1 to indicate that this requires you to get on a bus.
Now, imagine you find a path from (point A, free) to (point B, free) in this graph. This corresponds to getting on and off of a series of buses that start you at point A and end at point B, and the cost will be the number of different buses that you ended up getting on. If you run a shortest paths algorithm in this graph (say, Dijkstra's algorithm), you'll find the path from the start to end point that minimizes the number of bus transfers!
You could go through the array once, and keep a set of buses that are common to the visited stops. As soon as none such buses can be found, take the previous set, choose one bus from it, and fill the result with that bus for that many stops.
Then put all buses at the current stop in the set, and repeat the operation for the subsequent stops, ...etc.
Here is the algorithm coded in ES6 JavaScript. It uses a Set to allow constant-time access to the items (buses) it stores.
// Helper function: given a reduced set of buses, and a count,
// add one of those buses as the bus to take during that many stops
function addToResult(common, count, result) {
let bus = common.values().next().value; // pick any available bus
while (count > 0) {
result.push(bus);
count--;
}
}
// Main algorithm
function getBusRide(stops) {
if (stops.length === 0) return [];
let result = [],
count = 0,
common;
for (let buses of stops) {
if (count == 0) { // First iteration only
common = new Set(buses); // all buses are candidate
count = 1;
} else {
let keep = new Set();
for (let bus of buses) {
// Only keep buses as candidate when they
// are still served here
if (common.has(bus)) keep.add(bus);
}
if (keep.size == 0) { // Need to change bus
addToResult(common, count, result);
count = 0;
keep = new Set(buses); // all buses are candidate
}
common = keep;
count++;
}
}
addToResult(common, count, result);
return result;
}
// Sample input
const stops = [
[ 42, 45, 47],
[ 45, 47, 42, 88],
[ 21, 77, 42],
[ 22, 47, 42, 88],
[ 23, 47, 42],
[ 24, 47, 42, 8, 91],
[ 25, 13, 42, 3, 84],
[ 26, 10, 11, 4, 54],
[ 27, 9, 8, 88, 71]
];
// Apply the algorithm
console.log(getBusRide(stops));
.as-console-wrapper { max-height: 100% !important; top: 0; }
This algorithm runs in O(n) where n is the total number of values in the input, so in the example n = 37.
Related
Algorithm question
The following array exists list = [1, 3, 6, 8, 12, 18, 25, 28, 30, 40, 45, 50, 60, 68, 78, 88, 98, 128, 158, 198, 248, 298 , 348, 418, 488, 548, 588, 618, 648, 698, 798, 818, 848, 898, 998, 1048, 1098, 1148, 1198, 1248, 1298, 1398, 1448, 149, 8, 1998 , 2298, 2598, 2998, 3298, 3998, 4498, 4998, 5898, 6498], the target value is a number, you need to select the sum of n numbers from list as target, n The range is [1,10], where items in the list allow repeated selection, for example:
Example1: Assuming target = 10,
✅ Possible outcomes are as follows:
Result 1: 10*1 = 10, => [1,1,1,1,1,1,1,1,1,1]
Result 2: 1*8 + 2*1 = 10, => [8,1,1]
Result 3: 1*6 + 3*1 + 1*1 = 10, => [6,3,1]
Result 4: 3*3 + 1*1 = 10, => [3,3,3,1]
...
Example2: Assuming target = 20,
✅ Possible outcomes are as follows:
Result 1: 18*1 + 2*1 = 20, => [18,1,1]
Result 2: 12*1 + 8*1 = 20, => [12,8]
...
❌ Bad Result:
20 1s, against the rules: pick up to 10
[1,2,5], the sum is not right
Note
Pick a maximum of 10, a minimum of 1, repeatable picks
if no suitable result is found, return undefined
Solution
Solution1- Recursive computation - 10/25/2022
Recursion always solves this kind of problem, maybe there are other better ways, I will keep updating and trying.
TypeScript Playground - Recursive computation
Question
I want to find an optimal solution, speed first.
const list = [
1,
3,
6,
8,
12,
18,
25,
28,
30,
40,
45,
50,
60,
68,
78,
88,
98,
128,
158,
198,
248,
298,
348,
418,
488,
548,
588,
618,
648,
698,
798,
818,
848,
898,
998,
1048,
1098,
1148,
1198,
1248,
1298,
1398,
1448,
1498,
1598,
1648,
1998,
2298,
2598,
2998,
3298,
3998,
4498,
4998,
5898,
6498,
];
function getCombinations(
list: number[],
target: number
): Array<number> | undefined {
// TODO...
}
This is solvable with dynamic programming. I'll just outline the solution. I do have other posts where I've actually given working code for problems with similar ideas. For example Finding all possible combinations of numbers to reach a given sum.
First imagine we have a 2-D data structure with the following information in each Node:
{
is_root: true|false, // True only at the root of the data structure
current_sum: ..., // At this node, here is the current sum
solution_count: ..., // How many solutions below here
current_value: ..., // A value to find more solutions under
current_value_count: ..., // How many times this value has been used.
// First dimension, go back by current_value
prev_sum_solution: ptr to Node,
// Second dimension, go back to current sum, previous value
prev_value_solution: ptr to Node,
}
These nodes will hold ONLY information for solutions. You can report how many there are, or produce the solutions recursively. If node.prev_sum_solution is not null, then node.prev_sum_solution->solution_count gives how many solutions will have current_value next. And if f node.prev_value_solution is not null, then node.prev_value_solution->solution_count gives how many solutions will have current_sum with a previous value next. This allows you to find any particular solution as well.
Now how do we generate this solution? Well first we initialize an array of target+1 pointers to nodes:
solution = [null, null, ..., null]
And then we replace solution[0] with our root node:
{
is_root: true,
current_sum: 0,
solution_count: 1,
current_value: 0,
current_value_count: 0,
prev_sum_solution: null,
prev_value_solution: null,
}
(Warning. I'll use a mix of notations for this pseudo-code. And specifically . accesses an object's properties, while -> accesses properties from a pointer to an object. You can make the ideas work in any language whether or not it has pointers though.)
And then we go as follows:
for value in list:
// Add solutions that use this value
for i in range(n): // ie i is in {0, 1, 2, ..., n-1}
// We iterate down to avoid solutions that use this.
for (j = list.length-1; (i+1) * value <= j; j--) {
this_solution = solution[j]
prev_solution = solution[j-value]
if prev_solution == null or
(prev_solution.current_value != value and 0 < i) or
(prev_solution.current_value_count != i-i):
continue // can't add value here.
else:
if (this_solution == null) {
solution[j] = pointer to Node{
is_root: false,
current_sum: i,
solution_count: prev_solution->solution_count,
current_value: value,
current_value_count: i,
prev_sum_solution: prev_solution,
prev_value_solution: null,
}
}
else {
solution[i] = pointer to Node{
is_root: false,
current_sum: j,
solution_count: prev_solution->solution_count + this_solution->solution_count,
current_value: value,
current_value_count: i,
prev_sum_solution: prev_solution,
prev_value_solution: this_solution,
}
}
}
}
And when we are done, if we have no mistakes, solution[limit] will hold a pointer to a Node that has all the information about all of the possible solutions that exist.
Your shop sells several different types of dolls. Each doll has a suggested price, and no
two types of doll have the same price. You would like to fix an actual selling price for
each doll so that dolls of different types are as different in price as possible. Due to
some government regulations, you can only modify the suggested price within a fixed
band of ±K—in other words, if the suggested price is p, you can pick any selling price
in the range {p− K, p− K + 1, . . . , p+ K −1, p+ K}. Of course, the selling price must
always be non-negative.
For instance, suppose there are four types of dolls with suggested prices 130, 210, 70
and 90 and you are allowed to modify prices within a band of 20. Then, you can adjust
the prices to 150, 210, 50 and 100, respectively, so that the minimum difference in price
between any two types of dolls is 50. (For the second doll, you could have picked any
price between 200 and 230.) You can check that this is the largest separation that you
can achieve given the constraint.
In each of the cases below, you are given a sequence of prices and the value of K. You
have to determine the maximum separation that you can achieve between all pairs in
the sequence if you are allowed to modify each price by upto ±K.
(a) K = 13. Sequence: 144, 152, 214, 72, 256, 3, 39, 117, 238, 280.
(b) K = 10. Sequence: 10, 48, 57, 32, 61, 74, 33, 45, 99, 81, 19, 24, 101.
(c) K = 20. Sequence: 10, 19, 154, 67, 83, 39, 54, 110, 124, 99, 139, 170
So basically, I just need to find the value of maximum separation without coding. I tried to devise an algorithm, but failed miserably, so I just started brute forcing it, by basically increasing/decreasing each of the prices by a certain value, but the bruteforcing applied here is just too tough due to the value of K. (It would have been simple for any K<6).
Can someone define a function or recurrence relation to calculate it? The solutions are up online, but they only give the answer as an integer and don't explain how to reach the solution. I am a beginner in programming, so try explaining using pseudocode/ little bit of C++, please. Thank you.
Source: http://www.iarcs.org.in/inoi/2013/zio2013/zio2013-qpaper.pdf
Solution: http://www.iarcs.org.in/inoi/2013/zio2013/zio2013-solutions.pdf
Here is a O(nlogn) algorithm.
To illustrate I will use the second example: 10, 48, 57, 32, 61, 74, 33, 45, 99, 81, 19, 24, 101 with K=10
Sort the list (10, 19, 24, 32, 33, 45, 48, 57, 61, 74, 81, 99, 101)
Use bisection to find the minimum separation x
For a trial value of x, assign the final values greedily placing them as small as possible while satisfying the conditions (non-negative, within K of original value, at least x greater than previous).
So let us start with x=10.
We will move as follows:
10->0 (can't go negative so this is smallest allowed)
19->10 (can't go within K=10 of the previous value)
24->20
32->30
33->40
45->50
48 becomes impossible. We can only assign values between 38 and 58, but none of these are more than 10 away from the previous 50.
We conclude that x=10 is too high a separation and we need to move lower.
You might try x=7 and find it is possible, x=9 find it is impossible, then try x=8:
10->0
19->9 (can only move to values 9->29)
24->17
32->25
33->33
45->41
48->49
57->56
61->64
74->72
81->80
99->89
101->97
And so we have found that x=8 is possible, x=9 is impossible and therefore x=8 is the maximum possible separation.
This is effectively log base 2, but I do not have access to this functionality in the environment I'm in. Manually walking through the bits to verify them is unacceptably slow. If it were just 4 bits, I could probably index it and waste some space in an array, but with 64 bits it is not viable.
Any clever constant time method to find which bit is set ? (The quantity is a 64-bit number).
EDIT: To clarify, there is a single bit set in the number.
I assume you want the position of the most significant bit that is set. Do a binary search. If the entire value is 0, no bits are set. If the top 32 bits are 0, then the bit is in the bottom 32 bits; else it is in the high half. Then recurse on the two 16-bit halves of the appropriate 32 bits. Recurse until you are down to a 4-bit value and use your look-up table. (Or recurse down to a 1-bit value.) You just need to keep track of which half you used at each recursion level.
The fastest method I know of uses a DeBruijn Sequence.
Find the log base 2 of an N-bit integer in O(lg(N)) operations with multiply and lookup
Note that in lg(N), N is the number of bits, not the number of the highest set bit. So it's constant time for any N-bit number.
If you know that the number is an exact power of 2 (i.e. there is only 1 bit set), there is an even faster method just below that.
That hack is for 32 bits. I seem to recall seeing a 64 bit example somewhere, but can't track it down at the moment. Worst case, you run it twice: once for the high 32 bits and once for the low 32 bits.
If your numbers are powers of 2 and you have a bit count instruction you could do:
bitcount(x-1)
e.g.
x x-1 bitcount(x-1)
b100 b011 2
b001 b000 0
Note this will not work if the numbers are not powers of 2.
EDIT
Here is a 64bit version of the De Brujin method:
static const int log2_table[64] = {0, 1, 2, 7, 3, 13, 8, 19, 4, 25, 14, 28, 9, 34,
20, 40, 5, 17, 26, 38, 15, 46, 29, 48, 10, 31,
35, 54, 21, 50, 41, 57, 63, 6, 12, 18, 24, 27,
33, 39, 16, 37, 45, 47, 30, 53, 49, 56, 62, 11,
23, 32, 36, 44, 52, 55, 61, 22, 43, 51, 60, 42, 59, 58};
int fastlog2(unsigned long long x) {
return log2_table[ ( x * 0x218a392cd3d5dbfULL ) >> 58 ];
}
Test code:
int main(int argc,char *argv[])
{
int i;
for(i=0;i<64;i++) {
unsigned long long x=1ULL<<i;
printf("0x%llu -> %d\n",x,fastlog2(x));
}
return 0;
}
The magic 64bit number is an order 6 binary De Brujin sequence.
Multiplying by a power of 2 is equivalent to shifting this number up by a certain number of places.
This means that the top 6 bits of the multiplication result correspond to a different subsequence of 6 digits for each input number. The De Brujin sequence has the property that each subsequence is unique, so we can construct an appropriate lookup table to turn back from subsequence to position of the set bit.
If you use some modern Intel CPU, you can use hardware
supported "POPulation CouNT" assembly instruction:
http://en.wikipedia.org/wiki/SSE4#POPCNT_and_LZCNT
for Unix/gcc, you can use macro:
#include <smmintrin.h>
uint64_t x;
int c = _mm_popcnt_u64(x);
I'm putting together a simple chess position evaluation function. This being the first time for me building a chess engine, I am feeling very tentative with putting in just any evaluation function. The one shown on this Chess Programming Wiki page looks like a good candidate. However this has an ellipsis at the end which makes me unsure of whether it will be a good one to use?
Once the whole engine is in place and functional, I intend to come back to the evaluation function and make a real attempt to sorting it out properly. But for now I need some sort of function which is good enough to play against an average amateur.
The most basic component of an evaluation function is material, obviously. This should be perfectly straightforward, but on its own does not lead to interesting play. The engine has no sense of position at all, and simply reacts to tactical lines. But we will start here:
value = white_material - black_material // calculate delta material
Next we introduce some positional awareness through piece-square tables. For example, this is a such a predefined table for pawns:
pawn_table = {
0, 0, 0, 0, 0, 0, 0, 0,
75, 75, 75, 75, 75, 75, 75, 75,
25, 25, 29, 29, 29, 29, 25, 25,
4, 8, 12, 21, 21, 12, 8, 4,
0, 4, 8, 17, 17, 8, 4, 0,
4, -4, -8, 4, 4, -8, -4, 4,
4, 8, 8,-17,-17, 8, 8, 4,
0, 0, 0, 0, 0, 0, 0, 0
}
Note that this assumes the common centipawn (value of pawn is ~100) value system. For each white pawn we encounter, we index into the table with the pawn's square and add the corresponding value.
for each p in white pawns
value += pawn_table[square(p)]
Note that we can use use a simple calculation to reflect the table when indexing for black pieces. Alternatively you can define separate tables.
For simple evaluation this will work very well and your engine will probably already be playing common openings. However, it's not too hard to make some simple improvements. For example, you can create tables for the opening and the endgame, and interpolate between them using some sort of phase calculation. This is especially effective for kings, where their place shifts from the corners to the middle of the board as the game progresses.
Thus our evaluation function may look something like:
evaluate(position, colour) {
phase = total_pieces / 32 // this is just an example
opening_value += ... // sum of evaluation terms
endgame_value += ...
final_value = phase * opening_value + (1 - phase) * endgame_value
return final_value * sign(colour) // adjust for caller's perspective
}
This type of evaluation, along with quiescence search, should be enough to annihilate most amateurs.
There is a straight road with 'n' number of milestones. You are given
an array with the distance between all the pairs of milestones in
some random order. Find the position of milestones.
Example:
Consider a road with 4 milestones (a,b,c,d) :
a ---3Km--- b ---5Km--- c ---2Km--- d
Distance between a and b is 3
Distance between a and c is 8
Distance between a and d is 10
Distance between b and c is 5
Distance between b and d is 7
Distance between c and d is 2
All the above values are given in a random order say 7, 10, 5, 2, 8, 3.
The output must be 3, 5, 2 or 2, 5, 3.
Assuming the length of the give array is n. My idea is:
Calculate the number of milestones by solving a quadratic equation, saying it's x.
There are P(n, x-1) possibilities.
Validate every possible permutation.
Is there any better solution for this problem?
I can't find an algorithm for this that has good worst-case behaviour. However, the following heuristic may be useful for practical solution:
Say the first landmark is at position zero. You can find the last landmark. Then all other landmark positions need to appear in the input array. Their distances to the last landmark must also appear.
Let's build a graph on these possible landmark positions.
If a and b are two possible landmark positions, then either |a-b| appears in the input array or at least one of a and b isn't a landmark position. Draw an edge between a and b if |a-b| appears in the input array.
Iteratively filter out landmark positions whose degree is too small.
You wind up with something that's almost a clique-finding problem. Find an appropriately large clique; it corresponds to a positioning of the landmarks. Check that this positioning actually gives rise to the right distances.
At worst here, you've narrowed down the possible landmark positions to a more manageable set.
Ok. I will give my idea , which could reduce the number of permutations.
Finding n, is simple, you could even run a Reverse factorial https://math.stackexchange.com/questions/171882/is-there-a-way-to-reverse-factorials
Assumption:
Currently I have no idea of how to find the numbers. But I assume you have found out the numbers somehow. After finding n and elements we could apply this for partial reduction of computation.
Consider a problem like,
|<--3-->|<--6-->|<--1-->|<--7-->|
A B C D E
Now as you said, the sum they will give (in random order too) 3,9,10,17,6,7,14,1,8,7.
But you could take any combination (mostly it will be wrong ),
6-3-1-7. (say this is our taken combination)
Now,
6+3 -> 9 There, so Yes //Checking in the list whether the 2 numbers could possibly be adjacent.
3+1 -> 4 NOT THERE, so cannot
1+7 -> 8 There, So Yes
6+7 -> 13 NOT THERE, So cannot be ajacent
Heart concept :
For, 2 numbers to be adjacent, their sum must be there in the list. If the sum is not in the list, then the numbers are not adjacent.
Optimization :
So, 3 and 1 will not come nearby. And 6 and 7 will not come nearby.
Hence while doing permutation, we could eliminate
*31*,*13*,*76* and *67* combinations. Where * is 0 or more no of digits either preceding or succeeding.
i.e instead of trying permutation for 4! = 24 times, we could only check for 3617,1637,3716,1736. ie only 4 times. i.e 84% of computation is saved.
Worst case :
Say in your case it is 5,2,3.
Now, we have to perform this operation.
5+2 -> 7 There
2+3 -> 5 There
5+3 -> 8 There
Oops, your example is worst case, where we could not optimize the solution in these type of cases.
Place the milestones one by one
EDIT See new implementation below (with timings).
The key idea is the following:
Build a list of milestones one by one, starting with one milestone at 0 and a milestone at max(distances). Lets call them endpoints.
The largest distance that's not accounted for has to be from one of the endpoints, which leaves at most two positions for the corresponding milestone.
The following Python program simply checks if the milestone can be placed from the left endpoint, and if not, tries to place the milestone from the right endpoint (always using the largest distances that's not accounted for by the already placed milestones). This has to be done with back-tracking, as placements may turn out wrong later.
Note that there is another (mirrored) solution that is not output. (I don't think there can be more than 2 solutions (symmetric), but I haven't proven it.)
I consider the position of the milestones as the solution and use a helper function steps for the output desired by the OP.
from collections import Counter
def milestones_from_dists(dists, milestones=None):
if not dists: # all dist are acounted for: we have a solution!
return milestones
if milestones is None:
milestones = [0]
max_dist = max(dists)
solution_from_left = try_milestone(dists, milestones, min(milestones) + max_dist)
if solution_from_left is not None:
return solution_from_left
return try_milestone(dists, milestones, max(milestones) - max_dist)
def try_milestone(dists, milestones, new_milestone):
unused_dists = Counter(dists)
for milestone in milestones:
dist = abs(milestone - new_milestone)
if unused_dists[dist]:
unused_dists[dist] -= 1
if unused_dists[dist] == 0:
del unused_dists[dist]
else:
return None # no solution
return milestones_from_dists(unused_dists, milestones + [new_milestone])
def steps(milestones):
milestones = sorted(milestones)
return [milestones[i] - milestones[i - 1] for i in range(1, len(milestones))]
Example usage:
>>> print(steps(milestones_from_dists([7, 10, 5, 2, 8, 3])))
[3, 5, 2]
>>> import random
>>> milestones = random.sample(range(1000), 100)
>>> dists = [abs(x - y) for x in milestones for y in milestones if x < y]
>>> solution = sorted(milestones_from_dists(dists))
>>> solution == sorted(milestones)
True
>>> print(solution)
[0, 10, 16, 23, 33, 63, 72, 89, 97, 108, 131, 146, 152, 153, 156, 159, 171, 188, 210, 211, 212, 215, 219, 234, 248, 249, 273, 320, 325, 329, 339, 357, 363, 387, 394, 396, 402, 408, 412, 418, 426, 463, 469, 472, 473, 485, 506, 515, 517, 533, 536, 549, 586, 613, 614, 615, 622, 625, 630, 634, 640, 649, 651, 653, 671, 674, 697, 698, 711, 715, 720, 730, 731, 733, 747, 758, 770, 772, 773, 776, 777, 778, 783, 784, 789, 809, 828, 832, 833, 855, 861, 873, 891, 894, 918, 952, 953, 968, 977, 979]
>>> print(steps(solution))
[10, 6, 7, 10, 30, 9, 17, 8, 11, 23, 15, 6, 1, 3, 3, 12, 17, 22, 1, 1, 3, 4, 15, 14, 1, 24, 47, 5, 4, 10, 18, 6, 24, 7, 2, 6, 6, 4, 6, 8, 37, 6, 3, 1, 12, 21, 9, 2, 16, 3, 13, 37, 27, 1, 1, 7, 3, 5, 4, 6, 9, 2, 2, 18, 3, 23, 1, 13, 4, 5, 10, 1, 2, 14, 11, 12, 2, 1, 3, 1, 1, 5, 1, 5, 20, 19, 4, 1, 22, 6, 12, 18, 3, 24, 34, 1, 15, 9, 2]
New implementation incorporationg suggestions from the comments
from collections import Counter
def milestones_from_dists(dists):
dists = Counter(dists)
right_end = max(dists)
milestones = [0, right_end]
del dists[right_end]
sorted_dists = sorted(dists)
add_milestones_from_dists(dists, milestones, sorted_dists, right_end)
return milestones
def add_milestone
s_from_dists(dists, milestones, sorted_dists, right_end):
if not dists:
return True # success!
# find max dist that's not fully used yet
deleted_dists = []
while not dists[sorted_dists[-1]]:
deleted_dists.append(sorted_dists[-1])
del sorted_dists[-1]
max_dist = sorted_dists[-1]
# for both possible positions, check if this fits the already placed milestones
for new_milestone in [max_dist, right_end - max_dist]:
used_dists = Counter() # for backing up
for milestone in milestones:
dist = abs(milestone - new_milestone)
if dists[dist]: # this distance is still available
dists[dist] -= 1
if dists[dist] == 0:
del dists[dist]
used_dists[dist] += 1
else: # no solution
dists.update(used_dists) # back up
sorted_dists.extend(reversed(deleted_dists))
break
else: # unbroken
milestones.append(new_milestone)
success = add_milestones_from_dists(dists, milestones, sorted_dists, right_end)
if success:
return True
dists.update(used_dists) # back up
sorted_dists.extend(reversed(deleted_dists))
del milestones[-1]
return False
def steps(milestones):
milestones = sorted(milestones)
return [milestones[i] - milestones[i - 1] for i in range(1, len(milestones))]
Timings for random milestones in the range from 0 to 100000:
n = 10: 0.00s
n = 100: 0.05s
n = 1000: 3.20s
n = 10000: still takes too long.
The largest distance in the given set of distance is the distance between the first and the last milestone, i.e. in your example 10. You can find this in O(n) step.
For every other milestone (every one except the first or the last), you can find their distances from the first and the last milestone by looking for a pair of distances that sums up to the maximum distance, i.e. in your example 7+3 = 10, 8+2 = 10. You can find these pairs trivially in O(n^2).
Now if you think the road is from east to west, what remains is that for all the interior milestones (all but the first or the last), you need to know which one of the two distances (e.g. 7 and 3, or 8 and 2) is towards east (the other is then towards west).
You can trivially enumerate all the possibilities in time O(2^(n-2)), and for every possible orientation check that you get the same set of distances as in the problem. This is faster than enumerating through all permutations of the smallest distances in the set.
For example, if you assume 7 and 8 are towards west, then the distance between the two internal milestones is 1 mile, which is not in the problem set. So it must be 7 towards west, 8 towards east, leading to solution (or it's mirror)
WEST | -- 2 -- | -- 5 -- | -- 3 -- | EAST
For a larger set of milestones, you would just start guessing the orientation of the two distances to the endpoints, and whenever you product two milestones that have a distance between them that is not in the problem set, you backtrack.