Take an item, choose a random run and a random position inside that run. 'Put' the item in its place. The next run is non-random: take the 'mirrored' one. I mean if the first run was 0 - (1,36), then next is 7 - (263, 300). If the first is 5 - (187,225), then next is 2 - (74,110). The position in the 'mirrored' run can be random, but since run is pretty wide, I believe you should also divide run into sub-runs and mirror positions also.
import numpy as np
from random import randint
runs = np.array([(1,36), (37,73), (74,110), (111,148), (149,186), (187,225), (226,262), (263, 300)])
#number of runs
numOfRuns = len(runs)
#number of subruns in a run
numberOfSubruns = 4
#maximum error when 'mirroring'
maxMirroringError = 0
#number of items
numOfItems = 50
#number of positions, must be even
numOfPos = 6
#50 items, 6 positions each
items=np.zeros((numOfItems,numOfPos))
for ii in range(numOfItems):
#create an array containing "used" runs.
usedruns=np.zeros(numOfRuns,dtype=bool)
for jj in range(0,numOfPos,2):
while True:
#choose run index
run_ind = randint(0,numOfRuns-1)
#check if it is not used
if not usedruns[run_ind]:
break;
#make run used for this item
usedruns[run_ind] = True
#choose position index
pos_ind = randint(runs[run_ind][0],runs[run_ind][1])
#store the result
items[ii][jj] = pos_ind
#we need this adjustment in the case of maxMirroringError!=0
#or else we get an infinite loop
adjuster=0
while True:
#find mirrored run
mirrored_run_ind = numOfRuns - 1 - run_ind+ randint(-(maxMirroringError+adjuster),maxMirroringError+adjuster)
#apply constraints for mirrored_run_ind to be in [0,numOfRuns)
mirrored_run_ind = np.min([mirrored_run_ind,numOfRuns-1])
mirrored_run_ind = np.max([mirrored_run_ind,0])
adjuster +=1
if not usedruns[mirrored_run_ind]:
break;
#make run used for this item
usedruns[mirrored_run_ind] = True
#get size of a particular subrun
subrun_size = (runs[run_ind][1]-runs[run_ind][0]) // numberOfSubruns
#find subrun's index
subrun_ind = (pos_ind-runs[run_ind][0]) // subrun_size
#find mirrored subrun's index
mirrored_subrun_ind = numberOfSubruns - subrun_ind - 1
#apply constraints for mirrored_run_ind to be in [0,numberOfSubruns)
mirrored_subrun_ind = np.min([mirrored_subrun_ind,numberOfSubruns-1])
mirrored_subrun_ind = np.max([mirrored_subrun_ind,0])
#find mirrored pos
#lower bound
a = (runs[mirrored_run_ind][1]-runs[mirrored_run_ind][0]) * mirrored_subrun_ind // numberOfSubruns
#upper bound is lower bound + mirrored subrun size
b = (runs[mirrored_run_ind][1]-runs[mirrored_run_ind][0]) * (mirrored_subrun_ind + 1) // numberOfSubruns
#index itself
mirrored_pos_ind = runs[mirrored_run_ind][0] + randint(a,b)
#store the result
items[ii][jj+1] = mirrored_pos_ind
#check for negatives
if (items<0).any():
print ('negative value encountered!')
raise;
#check for same runs
storedruns=np.zeros(numOfRuns,dtype=bool)
for pos in items[ii]:
for kk in range(numOfRuns):
if pos>=runs[kk][0] and pos<=runs[kk][1]:
if storedruns[kk]:
print('Same runs for an item encountered!')
raise;
else:
storedruns[kk] = kk;
#print an average of position of an item
# print(np.average(items[ii,:]))
print('\n')
# print a standard deviation of average of items
print(np.std(np.average(items,axis=1)))
# print('\n')
# print(items)
You can change numberOfSubruns & maxMirroringError
and see what fits your case. Your case (total randomness) is numberOfSubruns=1, maxMirroringError = numOfRuns
(equals 8). If you set numberOfSubruns = 4, maxMirroringError = 0
, you will get average very close to 148.
For your convenience, I put standard deviation calculation of averaged positions to the end.