-1

I have an amount of ranges, each with a weight. Every point on the total range is scored by the sum of the weights of all the ranges the point falls into. I'd like to be able to cheaply find the total value of points, and would like to be able to find a maximum. Ideally, it would also be able to find the maximum for a set of (equidistantly) spaced points.

Unfortunately, I'm heavily limited by performance, and am struggling to find a good algorithm for this.

The only two decent solutions I could find are: - Bruteforce it by sampling a bunch of points. For each: check every range whether it fits, find the total value, then check if it's better than the best so far. Decent point samples can be found by taking the boundaries of the ranges. - Create a set of buckets. Iterate through all the ranges, adding a value to all the buckets that fit within the range. Then iterate through all the buckets to find the best one

Neither are fast enough for my liking (they have been tested), and the latter isn't continuous so has accuracy problems.

I'd be okay with getting a slightly inaccurate response as long as the performance is way better. What adds a bit of extra complexity to my particular case is that I'm actually dealing with angles, so the environment is modular. The ranges can't be ordered, and I need to ensure that a range going from 340 degrees to 20 degrees contains both a point at 350 and at 10 degrees. The angle-ranges I'm dealing with can't exceed 180 beyond degrees and only very rarely are above 90. The amount of ranges generally isn't very high (1-30), but I need to do this calculation a lot.

The language is Java if it matters.

  • 1
    Could you describe the problem more accurately? – harold Nov 28 '17 at 12:11
  • Can you give an example instead of looong explanation? – Sniper Nov 28 '17 at 12:26
  • I need to find a best firing angle. There are objects in my game world that the imaginary line from my location in the direction of the angle needs to intersect with because I want to hit or prevent hitting them. Some objects are worth more than others (hence the weights). Each object can be represented as a let-most and a right-most angle that will intersect, these are the ranges. In some cases, I need to have a set of lines intersect with as many of these as possible, imagine shotgun style fire with several bullets flying away at different, predictable, angles. – AnythingElse Nov 28 '17 at 12:28

2 Answers2

1

Make a list (array) of angle intervals. If interval finish value less than start value (20<340), add 360 to the finish (340, 380)

Make a list of pair (angle, +weight for start point or -weight for finish point).

Concatenate list with its copy to provide circular intersection. (It is possible to copy only part of list)

Sort them by angle (use +/- as secondary key in case of tie: - before +)

Make CurrWeight=0

Walk through the list, adding +/weight field to CurrWeight. Check for max value.

(Such approach works for linear lists, I tried to modify it for circular ones, perhaps I might miss some caveats)

MBo
  • 77,366
  • 5
  • 53
  • 86
  • That seems like it could work! Thanks! At least in terms of O notation this should be lower. I'll have to try it out since in practice the sort may end up being as expensive as the other methods. Just duplicating the ranges that cross the 0 angle and starting my value tracking below 0 should probably work (though results can't be achieved before getting past 0). – AnythingElse Nov 28 '17 at 13:46
  • Sorting is O(nlogn). Really you need to copy a part of list upto the last finish time (for example, limit 70 for last time 360+70) – MBo Nov 28 '17 at 16:42
  • IMHO, this duplication only makes things more complicated. What about this? 1. Determine the value for 0 degrees by iterating the original ranges once. 2. Treat all angles as numbers in interval `(0, 360)`. 3. For each start add `(x, +weight)`, for each end add `(x, -weight)` and sort this list as usual. Ignore circularity. – maaartinus Nov 29 '17 at 10:57
0

enter image description here

here, instead of the term 'edges', i should have better used the term 'boundaries', because it referes to interval boundaries

    import java.util.ArrayList;
import java.util.Iterator;
import java.util.SortedSet;
import java.util.TreeSet;

public class Main {

    ArrayList<Interval> intervals;

    public static void main(String args[]) {

        Main main = new Main();

        main.intervals = new ArrayList<Interval>();
        Interval i1 = new Interval(10, 30, 1);
        Interval i2= new Interval(20, 40, 1);
        Interval i3= new Interval(50, 60, 1);
        Interval i4= new Interval(0, 70, 1);

        main.intervals.add(i1);
        main.intervals.add(i2);
        main.intervals.add(i3);
        main.intervals.add(i4);

        Interval winningInterval = main.processIntervals(main.intervals);
        System.out.println("winning interval="+winningInterval);



    }


    public Interval processIntervals(ArrayList<Interval> intervals)
    {
        SortedSet<Integer> intervalEdges = new TreeSet<Integer>();

        for(int i = 0;i<intervals.size();i++)
        {
            Interval currentInterval = intervals.get(i);
            intervalEdges.add(currentInterval.a);
            intervalEdges.add(currentInterval.b);
        }

        System.out.println(intervalEdges);

        //edges stores the same data as intervalEdges, but for convenience, it is a list
        ArrayList<Integer> edges = new ArrayList<Integer>(intervalEdges);

        ArrayList<Interval> intersectionIntervals = new ArrayList<Interval>();

        for(int i=0; i<edges.size()-1;i++)
        {
            Interval newInterval = new Interval(edges.get(i), edges.get(i+1), 0);

            int score = 0; //the sum of the values of the overlapping intervals
            for(int j=0; j<intervals.size();j++)
            {
                if(newInterval.isIncludedInInterval(intervals.get(j)))
                    score = score+ intervals.get(j).val;
            }

            newInterval.val = score;
            intersectionIntervals.add(newInterval);
        }

        System.out.println(intersectionIntervals);

        int maxValue=0; //the maximum value of an interval
        Interval x = new Interval(-1,-1,0);//that interval with the maximum value
        for(int i=0; i<intersectionIntervals.size();i++)
            {
            if(intersectionIntervals.get(i).val > maxValue)
            {
                maxValue=intersectionIntervals.get(i).val;
                x=intersectionIntervals.get(i);
            }
            }


        return x;
    }



}


class Interval
{
    public int a, b, val;

    public Interval(int a, int b, int val) {
        super();
        this.a = a;
        this.b = b;
        this.val = val;

    }

    @Override
    public String toString() {
        return "Interval [a=" + a + ", b=" + b + ", val=" + val + "]";
    }

    boolean isIncludedInInterval(Interval y)
    {
        //returns true if current interval is included in interval y
        return this.a>=y.a && this.b<= y.b;
    }
}

gives the output

    [0, 10, 20, 30, 40, 50, 60, 70]
[Interval [a=0, b=10, val=1], Interval [a=10, b=20, val=2], Interval [a=20, b=30, val=3], Interval [a=30, b=40, val=2], Interval [a=40, b=50, val=1], Interval [a=50, b=60, val=2], Interval [a=60, b=70, val=1]]
winning interval=Interval [a=20, b=30, val=3]

This solves the case when the intervals are straight line intervals, and not angular intervals. I will come back with modifications to take into account the fact that x=x+360.

Newton fan 01
  • 484
  • 5
  • 14