Open In App

Python – Non-overlapping Random Ranges

Improve
Improve
Like Article
Like
Save
Share
Report

Sometimes, while working with Python, we can have problem in which we need to extract N random ranges which are non-overlapping in nature and with given range size. This can have applications in which we work with data. Lets discuss certain way in which this task can be performed.

Method : Using any() + randint() + loop This is brute force way in which this task can be performed. In this we extract random ranges using randint(), and check for non-repetition of number ranges using any() and loop. 

Python3




# Python3 code to demonstrate working of
# Non-overlapping Random Ranges
# Using loop + any() + randint()
import random
 
# initializing N
N = 7
 
# initializing K
K = 5
 
# Non-overlapping Random Ranges
# Using loop + any() + randint()
tot = 10000
res = set()
for _ in range(N):
  temp = random.randint(0, tot - K)
  while any(temp >= idx and temp <= idx + K for idx in res):
     temp = random.randint(0, tot - K)
  res.add(temp)
res = [(idx, idx + K) for idx in res]
 
# printing result
print("The N Non-overlapping Random ranges are : " + str(res))


Output:

The N Non-overlapping Random ranges are : [(5347, 5352), (7108, 7113), (5479, 5484), 
(1906, 1911), (2228, 2233), (5206, 5211), (3260, 3265)]

Time Complexity: O(n*n) where n is the total number of values in the list 
Auxiliary Space: O(n) where n is the total number of values in the list 

Approach 2 : Using bisect and randint
 

Python3




import random
import bisect
 
def random_ranges(n, k, tot):
  ranges = []
  starts = [random.randint(0, tot-k) for _ in range(n)]
  starts.sort()
  for i in range(1, n):
    if starts[i] <= starts[i-1] + k:
      starts[i] = starts[i-1] + k + 1
  for start in starts:
    ranges.append((start, start + k))
  return ranges
 
n = 7
k = 5
tot = 10000
print(random_ranges(n, k, tot))


Output

[(1940, 1945), (3328, 3333), (3769, 3774), (3879, 3884), (7356, 7361), (7471, 7476), (9240, 9245)]

This approach uses bisect module to sort the start positions of the ranges and randint from random module to generate random start positions. The code then checks if the current start position is less than or equal to the previous start position + k, if so, it adjusts the current start position to the previous start position + k + 1.

Time complexity: O(nlogn) for sorting and O(n) for looping, so O(nlogn + n) in total.

Auxiliary Space: O(n) for storing the start positions and ranges.

Method: Using List Comprehension

Generate a list of n random start points using list comprehension, where each start point is a random integer between 0 and tot-k inclusive.
Sort the start points in ascending order.
Generate a list of end points using list comprehension, where each end point is the corresponding start point plus k.
Use a loop to iterate through the start points and end points simultaneously.
If the current start point is less than or equal to the previous end point, then set the current start point to be the previous end point plus 1.
Return a list of tuples, where each tuple contains a start point and its corresponding end point.

Python3




import random
 
def random_ranges(n, k, tot):
    start_points = sorted([random.randint(0, tot-k) for _ in range(n)])
    end_points = [start + k for start in start_points]
    for i in range(1, n):
        if start_points[i] <= end_points[i-1]:
            start_points[i] = end_points[i-1] + 1
            end_points[i] = start_points[i] + k
    return [(start, end) for start, end in zip(start_points, end_points)]
 
n = 7
k = 5
tot = 10000
print(random_ranges(n, k, tot))


Output

[(81, 86), (1454, 1459), (3398, 3403), (4467, 4472), (7559, 7564), (7577, 7582), (9551, 9556)]

Time complexity: O(n log n), due to the sorting step.

Auxiliary space: O(n), to store the lists of start points and end points.

Method: Using Numpy

In this method, np.random.randint(0, tot-k, size=n) generates an array of n random integers between 0 and tot-k. np.sort() sorts the array in increasing order. start_points + k generates an array of end points. np.maximum(end_points[:-1], start_points[1:]) sets each end point to the maximum of its current value and the next start point, so that no two ranges overlap. list(zip(start_points, end_points)) combines the start points and end points into a list of tuples representing the random ranges.

Python3




# Python3 code to demonstrate working of
# Non-overlapping Random Ranges
# Using numpy
import numpy as np
 
def random_ranges(n, k, tot):
    start_points = np.sort(np.random.randint(0, tot-k, size=n))
    end_points = start_points + k
    end_points[:-1] = np.maximum(end_points[:-1], start_points[1:])
    return list(zip(start_points, end_points))
 
# initializing n
n = 7
 
# initializing k
k = 5
 
tot = 10000
 
#printing result
print(random_ranges(n, k, tot))


Output:

[(1098, 4710), (4710, 5609), (5609, 5706), (5706, 6048), (6048, 6577), (6577, 8902), (8902, 8907)]

Time complexity: O(n log n), dominated by the sorting step.
Auxiliary Space: O(n), where n is the number of ranges to generate.
 



Last Updated : 01 Jun, 2023
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads