Inverse square separation of boids repels boids unevenly - python

I'm new to programming and I'm trying to make a little boids algorithm in python, so far I've written a method to keep the boids apart from one another using an inverse square function, and it looks like this:
def separation(self, boids):
repulsion = Vector(0, 0)
magnitude = 0
for Boid in boids:
if Boid != self:
distance = Boid.position - self.position
if np.linalg.norm(distance) < 100:
magnitude = 100/(np.linalg.norm(distance) ** 2)
direction = math.atan2(distance.y, distance.x)
repulsion = repulsion - Vector(magnitude * cos(direction), magnitude * sin(direction))
return repulsion
Since there's no privilege or anything to one boid, any two boids should repel each other with the same amount of force. However, when I ran a test with 2 boids separated by 10 units and no initial velocity, one boid accelerated noticeably faster than the other. I traced the error to the distance variable which the boids use to calculate the strength of the repulsion, and I made both boids print this variable. On the first frame of time, one boid saw the other as 10 units away while the other saw it as 11 units away (actually, it's -11, but since it gets squared the sign doesn't matter). I then printed out their positions and subtracted them to manually calculate their distance values on the first frame of time to see if it had to do with the equation for distance, and it produced 10 and -10, the correct values. I've tried rewriting the distance variable as self.distance and then writing self.distance = Boid.distance to make both boids see each other as the same distance apart, but it made no difference.

You are updating each boid one at a time. Some of the boids see the others move before they have a chance to. This means they never saw the other as close as the other saw them. This is the source of the asymmetry.
The problem lies in when the result of the separation call is applied to the boid's position.
You need to update all of the positions simultaneously. That is you need all of the separation calls to be done before you update the positions in reaction to the repulsive force.
Think of it like this I have two variables
a = 1
b = 1
I want to add each of them to the other. If I do:
a += b
b += a
I get:
a == 2
b == 3
This isn't what I want.
I want them both to be 2.
If I do this instead like this:
(a, b) = (a + b, b + a)
I get what I wanted.
Now this fancy expression hides a truth. It can be written without as to show this.
a_ = a + b
b_ = b + a
a = a_
b = b_

Related

Not getting direction value in my velocity calculation

I am working on a ground station to track our future cubesat, one of the things needed is to calculate the doppler shift in the frequency as it passes over head. To test this we are using the ISS TLE. I am using Skyfield and it has been super helpful, but I am having a simple issue that I can't seem to figure out. I need the velocity of the object, which has been easily attained, but I also need the direction relative to my position. I assumed it would be part of the velocity vector, since velocity is both magnitude and direction. Maybe I'm missing something in the code that is obvious, the way it is fixed at the moment is to get the distance at two points in time and figure out if its closing in or distancing itself. Then I simply multiply the vector by -1 if its closing in, and 1 otherwise. I figured something like this would be handled with the .velocity function but it does not seem so.
diff = satObsDiff.at(ts_now)
diff1 = satObsDiff.at(ts_next)
velocity = diff.speed().km_per_s * 1000 #converts km to m
print("Velocity: ")
print(velocity)
adjusted_velocity = velocity
range1 = diff.distance().km
range2 = diff1.distance().km
change = (range1 - range2)*1000
direction = 1
if change >= 0:
direction = 1
else:
direction = -1
observed_freq = ((C/(C + (adjusted_velocity * direction))) * emitted_freq)
The dot product of two vectors is positive if they are pointing in the same direction, zero if they are at right angles, and negative if they are pointing away from each other. So I suspect you can tell an approaching from a receding satellite by computing the dot product of the relative position with the velocity and checking whether it is more or less than zero:
d = np.dot(velocity.km, diff.position.km)
if d > 0:
print('receding')
else:
print('approaching')
You can also toss in some division if you want to know how fast the range is changing:
https://stackoverflow.com/a/55226228/85360

Is there a more efficient algorithm to calculate the Manhattan distance of a 8-puzzle game?

I'm currently writing an algorithm that solves the 8-puzzle game through an A* search algorithm with Python. However, when I time my code, I find that get_manhattan_distance takes a really long amount of time.
I ran my code with cProfile for Python, and the results are below what is printed out by the program. Here is a gist for my issue.
I've already made my program more efficient by copying using Numpy Arrays instead of Python's lists. I don't quite know how to make this step more efficient. My current code for get_manhattan_distance is
def get_manhattan(self):
"""Returns the Manhattan heuristic for this board
Will attempt to use the cached Manhattan value for speed, but if it hasn't
already been calculated, then it will need to calculate it (which is
extremely costly!).
"""
if self.cached_manhattan != -1:
return self.cached_manhattan
# Set the value to zero, so we can add elements based off them being out of
# place.
self.cached_manhattan = 0
for r in range(self.get_dimension()):
for c in range(self.get_dimension()):
if self.board[r][c] != 0:
num = self.board[r][c]
# Solves for what row and column this number should be in.
correct_row, correct_col = np.divmod(num - 1, self.get_dimension())
# Adds the Manhattan distance from its current position to its correct
# position.
manhattan_dist = abs(correct_col - c) + abs(correct_row - r)
self.cached_manhattan += manhattan_dist
return self.cached_manhattan
The idea behind this is, the goal puzzle for a 3x3 grid is the following:
1 2 3
4 5 6
7 8
Where there is a blank tile (the blank tile is represented by a 0 in the int array). So, if we have the puzzle:
3 2 1
4 6 5
7 8
It should have a Manhattan distance of 6. This is because, 3 is two places away from where it should be. 1 is two places away from where it should be. 5 is one place away from where it should be, and 6 is one place away from where it should be. Hence, 2 + 2 + 1 + 1 = 6.
Unfortunately, this calculation takes a very long time because there are hundreds of thousands of different boards. Is there any way to speed this calculation up?
It looks to me like you should only need to calculate the full Manhattan distance sum for an entire board once - for the first board. After that, you're creating new Board entities from existing ones by swapping two adjacent numbers. The total Manhattan distance on the new board will differ only by the sum of changes in Manhattan distance for these two numbers.
If one of the numbers is the blank (0), then the total distance changes by minus one or one depending on whether the non-blank number moved closer to its proper place or farther from it. If both of the numbers are non-blank, as when you're making "twins", the total distance changes by minus two, zero, or two.
Here's what I would do: add a manhattan_distance = None argument to Board.__init__. If this is not given, calculate the board's total Manhattan distance; otherwise simply store the given distance. Create your first board without this argument. When you create a new board from an existing one, calculate the change in the total distance and pass the result in to the new board. (The cached_manhattan becomes irrelevant.)
This should reduce the total number of calculations involved with distance by quite a bit - I'd expect it to speed things up by several times, more the larger your board size.

Python Trig Functions Return Complex Numbers?

I am writing code that accepts the degree by which a motor turns and uses that data to calculate the distance covered by the wheels (using distance = no. of rotations * distance covered per rotation).
It then makes an error adjustment (taking into consideration environmental factors such as friction).
Finally, using trigonometry, it calculates the distance moved along the x-axis and y-axis.
All the above is done by the function straight contained within the class CoordinateManager. This function is called by an instance of another class.
class CoordinateManager:
goalcord = [20, 0]
def __init__(self):
self.curcord = [0, 0]
self.theta = 0
def get_compass_angle(self):
compass = Sensor(address='in2')
return compass.value(0)
def turn(self, iangle, fangle):
self.theta = self.theta + (fangle-iangle)
def straight(self, turnangle):
d = turnangle*2*3.14*2/360
d = 1.8120132*(d**0.8938054)
thetarad = radians(self.theta)
dx = d*sin(thetarad)
dy = d*cos(thetarad)
self.curcord[0] += dx
self.curcord[1] += dy
Printing both d and self.theta shows that they contain correct values.
This must mean that the array self.curcord has valid values too. However, this has not been the case. Printing the two elements of self.curcord outputs complex numbers (some big float + another big floatj).
I can think of no logical explanation for this other than that the trigonometric functions must be returning complex numbers. However, I think the chances that a python built-in lib function returns wrong values are extraordinarily slim.
Is there any logical error that I may be overlooking?
Edit: I just tried changing the last two lines to:
self.curcord[0] += dx
self.curcord[1] += dy
I just tried using .real when displaying the values. Even though the values are real now, they are still wrong. I will look further into whether this is caused by some calculation error.
Since you said in the comments above that turnangle can be any integer, the problem can be directly traced to this line:
d = 1.8120132*(d**0.8938054)
Since turnangle can be negative, the value of d before this line is executed can also be negative; a negative value raised to an arbitrary decimal power is in general complex.
Therefore the problem does not lie with the trig functions at all. The above also leads me to believe that when you said
Printing both d and self.theta shows that they contain correct values
... you only did so after this line:
d = turnangle*2*3.14*2/360
This would explain why you wrongly thought the problem must lie elsewhere.
UPDATE:
It is a very bad habit to set a variable to some function of itself like you did. Try to use a different variable name to avoid confusion - as you saw above I had to refer to "this line" rather than by their variable names.
Perhaps something like this would work, assuming that the behaviour of the motor is the same regardless of the sign of turnangle?
d = sign(d) * 1.8120132 * (abs(d) ** 0.8938054)

speeding up processing 5 million rows of coordinate data

I have a csv file with two columns (latitude, longitude) that contains over 5 million rows of geolocation data.
I need to identify the points which are not within 5 miles of any other point in the list, and output everything back into another CSV that has an extra column (CloseToAnotherPoint) which is True if there is another point is within 5 miles, and False if there isn't.
Here is my current solution using geopy (not making any web calls, just using the function to calculate distance):
from geopy.point import Point
from geopy.distance import vincenty
import csv
class CustomGeoPoint(object):
def __init__(self, latitude, longitude):
self.location = Point(latitude, longitude)
self.close_to_another_point = False
try:
output = open('output.csv','w')
writer = csv.writer(output, delimiter = ',', quoting=csv.QUOTE_ALL)
writer.writerow(['Latitude', 'Longitude', 'CloseToAnotherPoint'])
# 5 miles
close_limit = 5
geo_points = []
with open('geo_input.csv', newline='') as geo_csv:
reader = csv.reader(geo_csv)
next(reader, None) # skip the headers
for row in reader:
geo_points.append(CustomGeoPoint(row[0], row[1]))
# for every point, look at every point until one is found within 5 miles
for geo_point in geo_points:
for geo_point2 in geo_points:
dist = vincenty(geo_point.location, geo_point2.location).miles
if 0 < dist <= close_limit: # (0,close_limit]
geo_point.close_to_another_point = True
break
writer.writerow([geo_point.location.latitude, geo_point.location.longitude,
geo_point.close_to_another_point])
finally:
output.close()
As you might be able to tell from looking at it, this solution is extremely slow. So slow in fact that I let it run for 3 days and it still didn't finish!
I've thought about trying to split up the data into chunks (multiple CSV files or something) so that the inner loop doesn't have to look at every other point, but then I would have to figure out how to make sure the borders of each section checked against the borders of its adjacent sections, and that just seems overly complex and I'm afraid it would be more of a headache than it's worth.
So any pointers on how to make this faster?
Let's look at what you're doing.
You read all the points into a list named geo_points.
Now, can you tell me whether the list is sorted? Because if it was sorted, we definitely want to know that. Sorting is valuable information, especially when you're dealing with 5 million of anything.
You loop over all the geo_points. That's 5 million, according to you.
Within the outer loop, you loop again over all 5 million geo_points.
You compute the distance in miles between the two loop items.
If the distance is less than your threshold, you record that information on the first point, and stop the inner loop.
When the inner loop stops, you write information about the outer loop item to a CSV file.
Notice a couple of things. First, you're looping 5 million times in the outer loop. And then you're looping 5 million times in the inner loop.
This is what O(n²) means.
The next time you see someone talking about "Oh, this is O(log n) but that other thing is O(n log n)," remember this experience - you're running an n² algorithm where n in this case is 5,000,000. Sucks, dunnit?
Anyway, you have some problems.
Problem 1: You'll eventually wind up comparing every point against itself. Which should have a distance of zero, meaning they will all be marked as within whatever distance threshold. If your program ever finishes, all the cells will be marked True.
Problem 2: When you compare point #1 with, say, point #12345, and they are within the threshold distance from each other, you are recording that information about point #1. But you don't record the same information about the other point. You know that point #12345 (geo_point2) is reflexively within the threshold of point #1, but you don't write that down. So you're missing a chance to just skip over 5 million comparisons.
Problem 3: If you compare point #1 and point #2, and they are not within the threshold distance, what happens when you compare point #2 with point #1? Your inner loop is starting from the beginning of the list every time, but you know that you have already compared the start of the list with the end of the list. You can reduce your problem space by half just by making your outer loop go i in range(0, 5million) and your inner loop go j in range(i+1, 5million).
Answers?
Consider your latitude and longitude on a flat plane. You want to know if there's a point within 5 miles. Let's think about a 10 mile square, centered on your point #1. That's a square centered on (X1, Y1), with a top left corner at (X1 - 5miles, Y1 + 5miles) and a bottom right corner at (X1 + 5miles, Y1 - 5miles). Now, if a point is within that square, it might not be within 5 miles of your point #1. But you can bet that if it's outside that square, it's more than 5 miles away.
As #SeverinPappadeaux points out, distance on a spheroid like Earth is not quite the same as distance on a flat plane. But so what? Set your square a little bigger to allow for the difference, and proceed!
Sorted List
This is why sorting is important. If all the points were sorted by X, then Y (or Y, then X - whatever) and you knew it, you could really speed things up. Because you could simply stop scanning when the X (or Y) coordinate got too big, and you wouldn't have to go through 5 million points.
How would that work? Same way as before, except your inner loop would have some checks like this:
five_miles = ... # Whatever math, plus an error allowance!
list_len = len(geo_points) # Don't call this 5 million times
for i, pi in enumerate(geo_points):
if pi.close_to_another_point:
continue # Remember if close to an earlier point
pi0max = pi[0] + five_miles
pi1min = pi[1] - five_miles
pi1max = pi[1] + five_miles
for j in range(i+1, list_len):
pj = geo_points[j]
# Assumes geo_points is sorted on [0] then [1]
if pj[0] > pi0max:
# Can't possibly be close enough, nor any later points
break
if pj[1] < pi1min or pj[1] > pi1max:
# Can't be close enough, but a later point might be
continue
# Now do "real" comparison using accurate functions.
if ...:
pi.close_to_another_point = True
pj.close_to_another_point = True
break
What am I doing there? First, I'm getting some numbers into local variables. Then I'm using enumerate to give me an i value and a reference to the outer point. (What you called geo_point). Then, I'm quickly checking to see if we already know that this point is close to another one.
If not, we'll have to scan. So I'm only scanning "later" points in the list, because I know the outer loop scans the early ones, and I definitely don't want to compare a point against itself. I'm using a few temporary variables to cache the result of computations involving the outer loop. Within the inner loop, I do some stupid comparisons against the temporaries. They can't tell me if the two points are close to each other, but I can check if they're definitely not close and skip ahead.
Finally, if the simple checks pass then go ahead and do the expensive checks. If a check actually passes, be sure to record the result on both points, so we can skip doing the second point later.
Unsorted List
But what if the list is not sorted?
#RootTwo points you at a kD tree (where D is for "dimensional" and k in this case is "2"). The idea is really simple, if you already know about binary search trees: you cycle through the dimensions, comparing X at even levels in the tree and comparing Y at odd levels (or vice versa). The idea would be this:
def insert_node(node, treenode, depth=0):
dimension = depth % 2 # even/odd -> lat/long
dn = node.coord[dimension]
dt = treenode.coord[dimension]
if dn < dt:
# go left
if treenode.left is None:
treenode.left = node
else:
insert_node(node, treenode.left, depth+1)
else:
# go right
if treenode.right is None:
treenode.right = node
else:
insert_node(node, treenode.right, depth+1)
What would this do? This would get you a searchable tree where points could be inserted in O(log n) time. That means O(n log n) for the whole list, which is way better than n squared! (The log base 2 of 5 million is basically 23. So n log n is 5 million times 23, compared with 5 million times 5 million!)
It also means you can do a targeted search. Since the tree is ordered, it's fairly straightforward to look for "close" points (the Wikipedia link from #RootTwo provides an algorithm).
Advice
My advice is to just write code to sort the list, if needed. It's easier to write, and easier to check by hand, and it's a separate pass you will only need to make one time.
Once you have the list sorted, try the approach I showed above. It's close to what you were doing, and it should be easy for you to understand and code.
As the answer to Python calculate lots of distances quickly points out, this is a classic use case for k-D trees.
An alternative is to use a sweep line algorithm, as shown in the answer to How do I match similar coordinates using Python?
Here's the sweep line algorithm adapted for your questions. On my laptop, it takes < 5 minutes to run through 5M random points.
import itertools as it
import operator as op
import sortedcontainers # handy library on Pypi
import time
from collections import namedtuple
from math import cos, degrees, pi, radians, sqrt
from random import sample, uniform
Point = namedtuple("Point", "lat long has_close_neighbor")
miles_per_degree = 69
number_of_points = 5000000
data = [Point(uniform( -88.0, 88.0), # lat
uniform(-180.0, 180.0), # long
True
)
for _ in range(number_of_points)
]
start = time.time()
# Note: lat is first in Point, so data is sorted by .lat then .long.
data.sort()
print(time.time() - start)
# Parameter that determines the size of a sliding lattitude window
# and therefore how close two points need to be to be to get flagged.
threshold = 5.0 # miles
lat_span = threshold / miles_per_degree
coarse_threshold = (.98 * threshold)**2
# Sliding lattitude window. Within the window, observations are
# ordered by longitude.
window = sortedcontainers.SortedListWithKey(key=op.attrgetter('long'))
# lag_pt is the 'southernmost' point within the sliding window.
point = iter(data)
lag_pt = next(point)
milepost = len(data)//10
# lead_pt is the 'northernmost' point in the sliding window.
for i, lead_pt in enumerate(data):
if i == milepost:
print('.', end=' ')
milepost += len(data)//10
# Dec of lead_obs represents the leading edge of window.
window.add(lead_pt)
# Remove observations further than the trailing edge of window.
while lead_pt.lat - lag_pt.lat > lat_span:
window.discard(lag_pt)
lag_pt = next(point)
# Calculate 'east-west' width of window_size at dec of lead_obs
long_span = lat_span / cos(radians(lead_pt.lat))
east_long = lead_pt.long + long_span
west_long = lead_pt.long - long_span
# Check all observations in the sliding window within
# long_span of lead_pt.
for other_pt in window.irange_key(west_long, east_long):
if other_pt != lead_pt:
# lead_pt is at the top center of a box 2 * long_span wide by
# 1 * long_span tall. other_pt is is in that box. If desired,
# put additional fine-grained 'closeness' tests here.
# coarse check if any pts within 80% of threshold distance
# then don't need to check distance to any more neighbors
average_lat = (other_pt.lat + lead_pt.lat) / 2
delta_lat = other_pt.lat - lead_pt.lat
delta_long = (other_pt.long - lead_pt.long)/cos(radians(average_lat))
if delta_lat**2 + delta_long**2 <= coarse_threshold:
break
# put vincenty test here
#if 0 < vincenty(lead_pt, other_pt).miles <= close_limit:
# break
else:
data[i] = data[i]._replace(has_close_neighbor=False)
print()
print(time.time() - start)
If you sort the list by latitude (n log(n)), and the points are roughly evenly distributed, it will bring it down to about 1000 points within 5 miles for each point (napkin math, not exact). By only looking at the points that are near in latitude, the runtime goes from n^2 to n*log(n)+.0004n^2. Hopefully this speeds it up enough.
I would give pandas a try. Pandas is made for efficient handling of large amounts of data. That may help with the efficiency of the csv portion anyhow. But from the sounds of it, you've got yourself an inherently inefficient problem to solve. You take point 1 and compare it against 4,999,999 other points. Then you take point 2 and compare it with 4,999,998 other points and so on. Do the math. That's 12.5 trillion comparisons you're doing. If you can do 1,000,000 comparisons per second, that's 144 days of computation. If you can do 10,000,000 comparisons per second, that's 14 days. For just additions in straight python, 10,000,000 operations can take something like 1.1 seconds, but I doubt your comparisons are as fast as an add operation. So give it at least a fortnight or two.
Alternately, you could come up with an alternate algorithm, though I don't have any particular one in mind.
I would redo algorithm in three steps:
Use great-circle distance, and assume 1% error so make limit equal to 1.01*limit.
Code great-circle distance as inlined function, this test should be fast
You'll get some false positives, which you could further test with vincenty
A better solution generated from Oscar Smith. You have a csv file and just sorted it in excel it is very efficient). Then utilize binary search in your program to find the cities within 5 miles(you can make small change to binary search method so it will break if it finds one city satisfying your condition).
Another improvement is to set a map to remember the pair of cities when you find one city is within another one. For example, when you find city A is within 5 miles of city B, use Map to store the pair (B is the key and A is the value). So next time you meet B, search it in the Map first, if it has a corresponding value, you do not need to check it again. But it may use more memory so care about it. Hope it helps you.
This is just a first pass, but I've sped it up by half so far by using great_circle() instead of vincinty(), and cleaning up a couple of other things. The difference is explained here, and the loss in accuracy is about 0.17%:
from geopy.point import Point
from geopy.distance import great_circle
import csv
class CustomGeoPoint(Point):
def __init__(self, latitude, longitude):
super(CustomGeoPoint, self).__init__(latitude, longitude)
self.close_to_another_point = False
def isCloseToAnother(pointA, points):
for pointB in points:
dist = great_circle(pointA, pointB).miles
if 0 < dist <= CLOSE_LIMIT: # (0, close_limit]
return True
return False
with open('geo_input.csv', 'r') as geo_csv:
reader = csv.reader(geo_csv)
next(reader, None) # skip the headers
geo_points = sorted(map(lambda x: CustomGeoPoint(x[0], x[1]), reader))
with open('output.csv', 'w') as output:
writer = csv.writer(output, delimiter=',', quoting=csv.QUOTE_ALL)
writer.writerow(['Latitude', 'Longitude', 'CloseToAnotherPoint'])
# for every point, look at every point until one is found within a mile
for point in geo_points:
point.close_to_another_point = isCloseToAnother(point, geo_points)
writer.writerow([point.latitude, point.longitude,
point.close_to_another_point])
I'm going to improve this further.
Before:
$ time python geo.py
real 0m5.765s
user 0m5.675s
sys 0m0.048s
After:
$ time python geo.py
real 0m2.816s
user 0m2.716s
sys 0m0.041s
This problem can be solved with a VP tree. These allows querying data
with distances that are a metric obeying the triangle inequality.
The big advantage of VP trees over a k-D tree is that they can be blindly
applied to geographic data anywhere in the world without having to worry
about projecting it to a suitable 2D space. In addition a true geodesic
distance can be used (no need to worry about the differences between
geodesic distances and distances in the projection).
Here's my test: generate 5 million points randomly and uniformly on the
world. Put these into a VP tree.
Looping over all the points, query the VP tree to find any neighbor a
distance in (0km, 10km] away. (0km is not include in this set to avoid
the query point being found.) Count the number of points with no such
neighbor (which is 229573 in my case).
Cost of setting up the VP tree = 5000000 * 20 distance calculations.
Cost of the queries = 5000000 * 23 distance calculations.
Time for setup and queries is 5m 7s.
I am using C++ with GeographicLib for calculating distances, but
the algorithm can of course be implemented in any language and here's
the python version of GeographicLib.
ADDENDUM: The C++ code implementing this approach is given here.

Methodology: Recursive datasets - tree branch refinement

I have a number of sets of data. These sets contain numbers that specify how much points a user gains upon passing to the next index:
A = (2,[2],2,6,6,10)
B = (2,4,[4],2,5,7,7,6,10,12,10,6)
C = (2,3,[4],5,6,7,7,8,10)
In this example I use three sets but in the real problem it are far more sets (a variable amount). The [square] brackets mean that that is the current selected index, so the indexes specified above are: (1,2,2)
All these indexes together form a total that I can keep track of by grabbing it from a webpage (in this case the total is: (2+2)+(2+4+4)+(2+3+4) = 23). By keeping track of the total I know that the total changes with a number, let's call this number X.
Total: 23 -> 25 -> 30
X: +2 +5 (these are the numbers X I can keep track of, they are given, but variable)
In this example the first X is +2, this either means that A went from 1->2 or B from 2->3:
Case 1: A passes on
A = (2,2,[2],6,6,10)
B = (2,4,[4],4,5,7,7,6,10,12,10,6)
C = (2,3,[4],5,6,7,7,8,10)
Case 2: B passes on
A = (2,[2],2,6,6,10)
B = (2,4,4,[2],5,7,7,6,10,12,10,6)
C = (2,3,[4],5,6,7,7,8,10)
We know the next increase is +5, this either means that for case 1, C goes from 2 -> 3 or for case 2: B 3->4 or C 2 -> 3
Case 1: A increased => C increased
A = (2,2,[2],6,6,10)
B = (2,4,4,[2],5,7,7,6,10,12,10,6)
C = (2,3,4,[5],6,7,7,8,10)
Case 2: B increased => B increased
A = (2,[2],2,6,6,10)
B = (2,4,4,2,[5],7,7,6,10,12,10,6)
C = (2,3,[4],5,6,7,7,8,10)
Case 3: B increased => C increased
A = (2,[2],2,6,6,10)
B = (2,4,4,[2],5,7,7,6,10,12,10,6)
C = (2,3,4,[5],6,6,7,8,10)
Now what I need to write an algorithm for is to display EVERY possible combination of indexes as a result of the increases I=(+2,+5), note that it in reality these are variables: I=(+X, +Y, +Z, ...) and the depth is also variable.
Now the problem looks quite easy, but imagine the next increase being 7 resulting in I=(+2,+5,+7), then there only remains 1 case valid (B->B->B). In some way I thus need to write a big recursive function that re-evaluates all results and removes dead ends, for every following increase, but I'm not sure how to write such a function.
For extra clarification: imagine the tracked data going +2, +5, +6, +6 then this diagram shows me what I want accomplished:
Summary: The full problem with all its variables is thus:
N Sets of data:
A = (a1,a2,a3,a4,...)
B = (b1,b2,b3,b4,...)
...
N = (n1,n2,n3,n4,...)
A given array Z with the current selected indexes:
Z = ([A], [B], [C], ... , [N])
A given array I with increases with depth N:
I = (+X,+Y,...,+N)
Asked: possible new arrays Z (possible ways to get to the new total with given intervals using only the increases specified in the data sets)
What I want: How to write an algorithm for this purpose, I don't need you to write the algorithm, but a starting points would be nice, I'm kinda lost in the problem.
Note: due to this question being quite long and technical, it is possible that some minor mistakes got in, comment below and I'll try to solve them

Categories

Resources