简体   繁体   中英

Back-and-Forth Linspace Generator

I'm looking to have a generator function that returns points along a line, given a minimum-distance k . This is simple enough, and can be done with numpy as follows:

points = np.linspace(start, end, k)

However, I would like to generate the points as a sort of "increasing resolution", so that on a line from 0 to 1, the generator would yield:

1/2, 1/4, 3/4, 1/8, 3/8, 5/8, ...

Again, this is easy enough to do recursively (just accept the endpoints and call self on each half), but I'd like a generator that can achieve the same thing without having to fill an array with everything from the start, and without duplicate points.

What would be the best way to do this?

A way to achieve this is by using:

def infinite_linspace():
    den = 2
    while True:
        for i in range(1,den,2):
            yield i/den
        den <<= 1

Here we thus iterate with the numerator from 1 to den-1 (inclusive), and then double the denominator .

The first 15 numbers are then:

>>> list(islice(infinite_linspace(), 15))
[0.5, 0.25, 0.75, 0.125, 0.375, 0.625, 0.875, 0.0625, 0.1875, 0.3125, 0.4375, 0.5625, 0.6875, 0.8125, 0.9375]
>>> [1/2,1/4,3/4,1/8,3/8,5/8,7/8,1/16,3/16,5/16,7/16,9/16,11/16,13/16,15/16]
[0.5, 0.25, 0.75, 0.125, 0.375, 0.625, 0.875, 0.0625, 0.1875, 0.3125, 0.4375, 0.5625, 0.6875, 0.8125, 0.9375]

We can even put more intelligence into it to obtain the i -th element relatively fast as well:

class Linspace:

    def __iter__(self):
        den = 2
        while True:
            for i in range(1,den,2):
                yield i/den
            den <<= 1

    def __getitem__(self, idx):
        if not isinstance(idx, int):
            raise TypeError('idx should be an integer')
        if idx < 0:
            raise ValueError('idx should be positive')
        den = denn = idx+1
        denn |= den >> 1
        while den != denn:
            den = denn
            denn |= denn >> 1
        denn += 1
        return (2*idx+3-denn)/denn

So now we can access in logarithmic time for instance the 10-th, 15-th and 123'456-th element:

>>> l = Linspace()
>>> l[9]
0.3125
>>> l[14]
0.9375
>>> l[123455]
0.8837966918945312

Here is a shorter, pseudo O(1) way of directly computing the i-th element:

def jumpy(i):
    i = (i<<1) + 3
    return i / (1<<i.bit_length()-1) - 1

list(map(jumpy, range(15)))
# [0.5, 0.25, 0.75, 0.125, 0.375, 0.625, 0.875, 0.0625, 0.1875, 0.3125, 0.4375, 0.5625, 0.6875, 0.8125, 0.9375]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM