This is a bit tricky. You can't always get logarithmically spaced numbers. As in your example, first part is rather linear. If you are OK with that, I have a solution. But for the solution, you should understand why you have duplicates.
Logarithmic scale satisfies the condition:
s[n+1]/s[n] = constant
Let's call this constant r
for ratio
. For n
of these numbers between range 1...size
, you'll get:
1, r, r**2, r**3, ..., r**(n-1)=size
So this gives you:
r = size ** (1/(n-1))
In your case, n=100
and size=10000
, r
will be ~1.0974987654930561
, which means, if you start with 1
, your next number will be 1.0974987654930561
which is then rounded to 1
again. Thus your duplicates. This issue is present for small numbers. After a sufficiently large number, multiplying with ratio will result in a different rounded integer.
Keeping this in mind, your best bet is to add consecutive integers up to a certain point so that this multiplication with the ratio is no longer an issue. Then you can continue with the logarithmic scaling. The following function does that:
import numpy as np
def gen_log_space(limit, n):
result = [1]
if n>1: # just a check to avoid ZeroDivisionError
ratio = (float(limit)/result[-1]) ** (1.0/(n-len(result)))
while len(result)<n:
next_value = result[-1]*ratio
if next_value - result[-1] >= 1:
# safe zone. next_value will be a different integer
result.append(next_value)
else:
# problem! same integer. we need to find next_value by artificially incrementing previous value
result.append(result[-1]+1)
# recalculate the ratio so that the remaining values will scale correctly
ratio = (float(limit)/result[-1]) ** (1.0/(n-len(result)))
# round, re-adjust to 0 indexing (i.e. minus 1) and return np.uint64 array
return np.array(list(map(lambda x: round(x)-1, result)), dtype=np.uint64)
Python 3 update: Last line used to be return np.array(map(lambda x: round(x)-1, result), dtype=np.uint64)
in Python 2
Here are some examples using it:
In [157]: x = gen_log_space(10000, 100)
In [158]: x.size
Out[158]: 100
In [159]: len(set(x))
Out[159]: 100
In [160]: y = gen_log_space(2000, 50)
In [161]: y.size
Out[161]: 50
In [162]: len(set(y))
Out[162]: 50
In [163]: y
Out[163]:
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11,
13, 14, 17, 19, 22, 25, 29, 33, 38, 43, 49,
56, 65, 74, 84, 96, 110, 125, 143, 164, 187, 213,
243, 277, 316, 361, 412, 470, 536, 612, 698, 796, 908,
1035, 1181, 1347, 1537, 1753, 1999], dtype=uint64)
And just to show you how logarithmic the results are, here is a semilog plot of the output for x = gen_log_scale(10000, 100)
(as you can see, left part is not really logarithmic):
