I want to iterate over an array in a certain fashion:
Starting with the first and the last element of the array, the next element I want to visit is the one furthest from all previously visited elements.
For an array of length n+1, the sequence would be
- 0,
- n,
- n/2 (furthest from 0 and n),
- n/4 and n*3/4 (furthest from all 3 previous indices),
- n/8, n*3/8, n*5/8, n*7/8, (furthest from all 5 previous indices)
- n*1/16, n*3/16, n*5/16, n*7/16, n*9/16, n*11/16, n*13/16, n*15/16
- ...
if n is not a power of two, then some of these numbers will have to be rounded up or down, but I am not sure how to avoid duplicates when rounding.
At the end I want an integer sequence that contains all the numbers between 0 and n exactly once. (For any n, not just powers of two)
Is there a name for this permutation?
How would a function that generates these numbers work?
I am looking for a function that can generate these numbers on-the-fly.
If there are a billion elements, I do not want to manage a giant list of all previously visited elements, or generate the whole permutation list in advance.
The idea is that I can abort the iteration once I have found an element that fits certain criteria, so I will in most cases not need the whole permutation sequence.
So I am looking for a function f(int currentIndex, int maxIndex)
with the following properties:
To interate over an array of size 8, i would call
f(0,8) returns 0, to get the index of the first element
f(1,8) returns 8
f(2,8) returns 4
f(3,8) returns 2
f(4,8) returns 6
f(5,8) returns 1
f(6,8) returns 3
f(7,8) returns 5
f(8,8) returns 7
(I am not quite sure how to extend this example to numbers that are not a power of two)
Is there a function with these properties?
I see how to do this, but it's tricky to describe.. bear with me.
The key idea is to logically partition your array into two sets: One contains a number of elements equal to the greatest power of two still less than the size of the array, and the other contains everything else. (So, if your array holds 29 elements, you'd have one with 16 and the other with 13.) You want these to be mixed as fairly as possible, and you want:
i-th
element of the first logical set (equivalently: How many elements of the second set come before thei-th
element of the first set)i
belongs to the first or second logical set.You then run the "Ideal" function you described over the first set (mapping with function 1, above), then do a single pass over the remaining elements. So long as you distribute fairly between the logical set, this will do as you describe.
To (logically) describe which indices belong to which partition: Call the size of the first logical partition
k
and the size of the second partitionj
. Assume that every element of the first set hasj/k
units of "credit" associated with it. Begin filling the true array with elements of the logical array, adding up credit as you go, but every time you would get to more than one unit of credit, place an element from the second array instead, and reduce the stored credit by one. This will fairly distribute exactlyj
elements from the second array betweenk
elements of the first array. NOTE: You don't actually perform this calculation, it's just a logical definition.With a little arithmetic, you can use this to implement the functions I described above. Before the
i-th
element of the first set will be exactlyfloor(i * j/k)
elements of the second set. You only run the second function during the final pass, so you can run that exactly from the definition.Does this make sense? I'm sure this will work, but it's difficult to describe.
Could you not use an array such that array[n][i]
such that
'use dynamic iteration such that you know the size going into the array i.e. nextGen=Toint(Ubound(Array)/2)
I was able to solve this myself, with the tips given by Paddy3118 and Edward Peters.
I now have a method that generates a Van der Corput permutation for a given range, with no duplicates and no missed values, and with constant and negligible memory requirements and good performance.
The method uses a c# iterable to generate the sequence on the fly.
The method
VanDerCorputPermutation()
takes two parameters, the upper exclusive bound of the range, and the base that should be used for generating the sequence. By default, base 2 is used.If the range is not a power of the given base, then the next larger power is used internally, and all indices that would be generated outside the range are simply discarded.
Usage:
The code itself uses only integer arithemtic and very few divisions:
Yes, it is called partitioning.
It is a very common methodology for searching in an ordered array.
also, it is used by QuickSort algorithm.
it mostly being implemented as a Recursive function that samples the "center" element, and then recurse on the "left" collection, then the "right" collection.
if the array is of length
1
, sample it and don't recurse.in the following example, i just search the array in the order you describe,
if the array was ordered, after checking the first pivot, i would have skipped checking the RightPart, or the LeftPart depending on the pivot value.
The hopping about you describe is a feature of the Van der Corput sequence, as mentioned in a task I wrote on Rosetta Code.
I have an exact function to re-order an input sequence, but it needs arrays as large as the input array.
What follows is an approximate solution that yields indices one by one and only takes the length of the input array, then calculates the indices with constant memory.
The testing gives some indication of how "good" the routine is.