Algorithm to calculate number of intersecting discs

前端 未结 30 1368
鱼传尺愫
鱼传尺愫 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),    
    

提交回复
热议问题