Algorithm to calculate number of intersecting discs

前端 未结 30 1377
鱼传尺愫
鱼传尺愫 2020-12-12 10:57

Given an array A of N integers we draw N discs in a 2D plane, such that i-th disc has center in (0,i) and a radius

相关标签:
30条回答
  • 2020-12-12 11:37

    Python 100 / 100 (tested) on codility, with O(nlogn) time and O(n) space.

    Here is @noisyboiler's python implementation of @Aryabhatta's method with comments and an example. Full credit to original authors, any errors / poor wording are entirely my fault.

    from bisect import bisect_right
    
    def number_of_disc_intersections(A):
        pairs = 0
    
        # create an array of tuples, each containing the start and end indices of a disk
        # some indices may be less than 0 or greater than len(A), this is fine!
        # sort the array by the first entry of each tuple: the disk start indices
        intervals = sorted( [(i-A[i], i+A[i]) for i in range(len(A))] )
    
        # create an array of starting indices using tuples in intervals
        starts = [i[0] for i in intervals]
    
        # for each disk in order of the *starting* position of the disk, not the centre
        for i in range(len(starts)):
    
            # find the end position of that disk from the array of tuples
            disk_end = intervals[i][1]
    
            # find the index of the rightmost value less than or equal to the interval-end
            # this finds the number of disks that have started before disk i ends
            count = bisect_right(starts, disk_end )
    
            # subtract current position to exclude previous matches
            # this bit seemed 'magic' to me, so I think of it like this...
            # for disk i, i disks that start to the left have already been dealt with
            # subtract i from count to prevent double counting
            # subtract one more to prevent counting the disk itsself
            count -= (i+1)
            pairs += count
            if pairs > 10000000:
                return -1
        return pairs
    

    Worked example: given [3, 0, 1, 6] the disk radii would look like this:

    disk0  -------         start= -3, end= 3
    disk1      .           start=  1, end= 1
    disk2      ---         start=  1, end= 3
    disk3  -------------   start= -3, end= 9
    index  3210123456789   (digits left of zero are -ve)
    
    intervals = [(-3, 3), (-3, 9), (1, 1), (1,3)]
    starts    = [-3, -3, 1, 1]
    
    the loop order will be: disk0, disk3, disk1, disk2
    
    0th loop: 
        by the end of disk0, 4 disks have started 
        one of which is disk0 itself 
        none of which could have already been counted
        so add 3
    1st loop: 
        by the end of disk3, 4 disks have started 
        one of which is disk3 itself
        one of which has already started to the left so is either counted OR would not overlap
        so add 2
    2nd loop: 
        by the end of disk1, 4 disks have started 
        one of which is disk1 itself
        two of which have already started to the left so are either counted OR would not overlap
        so add 1
    3rd loop: 
        by the end of disk2, 4 disks have started
        one of which is disk2 itself
        two of which have already started to the left so are either counted OR would not overlap
        so add 0
    
    pairs = 6
    to check: these are (0,1), (0,2), (0,2), (1,2), (1,3), (2,3),    
    
    0 讨论(0)
  • 2020-12-12 11:37

    I know that this is an old questions but it is still active on codility.

        private int solution(int[] A)
        {
            int openedCircles = 0;
            int intersectCount = 0;
    

    We need circles with their start and end values. For that purpose I have used Tuple. True/False indicates if we are adding Circle Starting or Circle Ending value.

            List<Tuple<decimal, bool>> circles = new List<Tuple<decimal, bool>>();
            for(int i = 0; i < A.Length; i ++)
            {
                // Circle start value
                circles.Add(new Tuple<decimal, bool>((decimal)i - (decimal)A[i], true));
    
                // Circle end value
                circles.Add(new Tuple<decimal, bool>((decimal)i + (decimal)A[i], false));
            }
    

    Order "circles" by their values. If one circle is ending at same value where other circle is starting, it should be counted as intersect (because of that "opening" should be in front of "closing" if in same point)

            circles = circles.OrderBy(x => x.Item1).ThenByDescending(x => x.Item2).ToList();
    

    Counting and returning counter

            foreach (var circle in circles)
            {
                // We are opening new circle (within existing circles)
                if(circle.Item2 == true)
                {
                    intersectCount += openedCircles;
    
                    if (intersectCount > 10000000)
                    { 
                        return -1;
                    }
    
                    openedCircles++;
                }
                else
                {
                    // We are closing circle
                    openedCircles--;
                }
            }
    
            return intersectCount;
        }
    
    0 讨论(0)
  • 2020-12-12 11:37

    Here is the PHP code that scored 100 on codility:

    $sum=0;
    
    //One way of cloning the A:
    $start = array();
    $end = array();
    
    foreach ($A as $key=>$value)
    {
    $start[]=0;
    $end[]=0;   
    }  
    
    for ($i=0; $i<count($A); $i++)
    {
      if ($i<$A[$i]) 
      $start[0]++;
      else        
      $start[$i-$A[$i]]++;
    
      if ($i+$A[$i] >= count($A))   
      $end[count($A)-1]++;
      else
      $end[$i+$A[$i]]++;
    }
    $active=0;
    for ($i=0; $i<count($A);$i++)
    {
      $sum += $active*$start[$i]+($start[$i]*($start[$i]-1))/2;
      if ($sum>10000000) return -1;
      $active += $start[$i]-$end[$i];
    }
    return $sum;
    

    However I dont understand the logic. This is just transformed C++ code from above. Folks, can you elaborate on what you were doing here, please?

    0 讨论(0)
  • 2020-12-12 11:37

    Here's a two-pass C++ solution that doesn't require any libraries, binary searching, sorting, etc.

    int solution(vector<int> &A) {
        #define countmax 10000000
        int count = 0;
        // init lower edge array
        vector<int> E(A.size());
        for (int i = 0; i < (int) E.size(); i++)
            E[i] = 0;
        // first pass
        // count all lower numbered discs inside this one
        // mark lower edge of each disc
        for (int i = 0; i < (int) A.size(); i++)
        {
            // if disc overlaps zero
            if (i - A[i] <= 0)
                count += i;
            // doesn't overlap zero
            else {   
                count += A[i];
                E[i - A[i]]++;
            }
            if (count > countmax)
                return -1;
        }
        // second pass
        // count higher numbered discs with edge inside this one
        for (int i = 0; i < (int) A.size(); i++)
        {
            // loop up inside this disc until top of vector
            int jend = ((int) E.size() < (long long) i + A[i] + 1 ? 
                        (int) E.size() : i + A[i] + 1);
            // count all discs with edge inside this disc
            // note: if higher disc is so big that edge is at or below 
            // this disc center, would count intersection in first pass
            for (int j = i + 1; j < jend; j++)
                count += E[j];
            if (count > countmax)
                return -1;
        }
        return count;
    }
    
    0 讨论(0)
  • 2020-12-12 11:37

    C# 100/100 with O(N*log(N)) time complexity and O(N) space complexity.

    The main ideas:

    1. Make 2 sorted arrays: left points and right points.
    2. Merge these arrays into one boolean array where true means "opening" and false means "closing" an interval.
    3. Run through the boolean array and count opened intervals, sum them up.

    _

    public int solution(int[] A) 
    {        
        var sortedStartPoints = A.Select((value, index) => (long)index-value).OrderBy(i => i).ToArray();
        var sortedEndPoints = A.Select((value, index) => (long)index+value).OrderBy(i => i).ToArray();     
    
        // true - increment, false - decrement, null - stop
        var points = new bool?[2*A.Length];
    
        // merge arrays
        for(int s=0, e=0, p=0; p < points.Length && s < sortedStartPoints.Length; p++)
        {
            long startPoint = sortedStartPoints[s];
            long endPoint = sortedEndPoints[e];
            if(startPoint <= endPoint)
            {
                points[p] = true;
                s++;
            }
            else
            {
                points[p] = false;
                e++;
            }
        }
    
        int result = 0;
        int opened = 0;
        // calculate intersections
        foreach(bool? p in points.TakeWhile(_ => _.HasValue))
        {
            if(result > 10000000)
                return -1;
    
            if(p == true)
            {
                result += opened;
                opened++;
            }
            else
            {                
                opened--;
            }
        }
    
        return result;
    }
    
    0 讨论(0)
  • 2020-12-12 11:38
    count = 0
    for (int i = 0; i < N; i++) {
      for (int j = i+1; j < N; j++) {
        if (i + A[i] >= j - A[j]) count++;
      }
    }
    

    It is O(N^2) so pretty slow, but it works.

    0 讨论(0)
提交回复
热议问题