我想以某种方式迭代数组:
从数组的第一个和最后一个元素开始,我想访问的下一个元素是离所有先前访问过的元素最远的元素。
对于长度为n + 1的数组,序列为
如果n不是2的幂,则其中一些数字必须向上或向下舍入,但我不确定在舍入时如何避免重复。
最后我想要一个整数序列,它包含0和n之间的所有数字恰好一次。 (对于任何n,而不仅仅是2的幂)
这个排列是否有名称?
生成这些数字的函数如何工作?
我正在寻找能够即时生成这些数字的功能。
如果有十亿个元素,我不想管理所有先前访问过的元素的巨大列表,或者提前生成整个排列列表。
我的想法是,一旦找到符合某些标准的元素,我就可以中止迭代,所以在大多数情况下我不需要整个排列序列。
所以我正在寻找具有以下属性的函数f(int currentIndex, int maxIndex)
:
要对大小为8的数组进行交互,我会调用
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
(我不太确定如何将此示例扩展为不是2的幂的数字)
是否有具有这些属性的函数?
答案 0 :(得分:1)
你所描述的跳跃是Van der Corput序列的一个特征,如a task I wrote on Rosetta Code中所述。
我有一个确切的函数来重新排序输入序列,但它需要与输入数组一样大的数组。
接下来是一个近似解决方案,它逐个产生索引,只取输入数组的长度,然后用常量内存计算索引。
测试给出了一些关于例程如何“好”的指示。
{{1}}
答案 1 :(得分:0)
你能不能使用数组[n] [i]
这样
Array [0][i] = "1,2,3,4,5,6,7" 'start
Array [1][i] = "1,2,3,4" '1st gen split 1
Array [2][i] = "4,5,6,7" '1st gen split 2
Array [3][i] = "1,2" '2nd gen split 1 split 1
Array [4][i] = "3,4" '2nd gen split 1 split 2
Array [5][i] = "4,5" '2nd gen split 2 split 1
Array [6][i] = "6,7" '2nd gen split 2 split 1
'使用动态迭代,以便知道进入数组的大小,即nextGen = Toint(Ubound(Array)/ 2)
If(
last(Array[n][i]) = first(Array[n+1][i]
then Pop(Array[n+1][i])
)
答案 2 :(得分:0)
我知道如何做到这一点,但描述一下是很棘手的..忍受我。
关键思想是将数组逻辑分为两组:一组包含多个元素,其中两个元素的最大幂仍然小于数组的大小,另一个包含其他所有元素。 (所以,如果你的数组包含29个元素,那么你有一个16个,另一个有13个。)你希望这些元素尽可能公平地混合,你想要:
i-th
元素的索引
逻辑集(等效于:第二组中有多少元素位于第一组的i-th
元素之前)i
是否属于的函数
到第一个或第二个逻辑集。然后你运行"理想"您在第一组中描述的函数(使用上面的函数1进行映射),然后对其余元素进行单次传递。只要您在逻辑集之间公平分配,就可以按照您的描述进行。
(逻辑上)描述哪些索引属于哪个分区:调用第一个逻辑分区k
的大小和第二个分区j
的大小。假设第一组的每个元素都有j/k
单位的" credit"与之相关联。开始使用逻辑数组的元素填充真实数组,随时添加信用,但每次获得多个信用单位时,请从第二个数组中放置一个元素,并将存储的信用减少一个。这将在第一个数组的j
元素之间公平地分配来自第二个数组的k
元素。注意:您实际上并未执行此计算,它只是一个逻辑定义。
通过一点算术,您可以使用它来实现我上面描述的功能。在第一组的i-th
元素与第二组的floor(i * j/k)
元素完全相同之前。您只能在最后一次传递期间运行第二个函数,因此您可以从定义中完全运行该函数。
这有意义吗?我确信这会奏效,但很难描述。
答案 3 :(得分:0)
是的,它被称为分区 在有序数组中搜索是一种非常常见的方法 此外,它由QuickSort算法使用。
它主要是作为递归函数实现的,它对" center"进行采样。元素,然后递归"左"收集,然后"权利"集合。
如果数组长度为1
,则对其进行采样并且不要递归。
在以下示例中,我只按您描述的顺序搜索数组,
如果数组是有序的,在检查第一个数据透视后,我会跳过检查RightPart或LeftPart,具体取决于数据透视值。
int partition(int* arr, int min, int max, int subject)
{ // [min, max] inclusive!
int pivot = (max - min + 1) >> 1; // (max - min)/2
if(arr[pivot] == subject)
return pivot;
if(pivot > 0)
{
int leftPart = partition(arr, min, pivot - 1, subject);
if(leftPart >= 0)
return leftPart;
}
if(max - pivot > 0)
{
int rightPart = partition(arr, pivot + 1, max, subject);
if(rightPart >= 0)
return rightPart;
}
return -1; // not found
}
int myArr[10] = {4,8,11,7,2,88,42,6,5,11 };
int idxOf5 = partition(myArr, 0, 9, 5);
答案 4 :(得分:0)
我自己能够用Paddy3118和Edward Peters给出的提示解决这个问题。
我现在有一种方法可以为给定范围生成Van der Corput排列,没有重复项且没有遗漏值,并且具有恒定且可忽略的内存要求和良好的性能。
该方法使用c#iterable来动态生成序列。
方法VanDerCorputPermutation()
采用两个参数,范围的上限,以及应该用于生成序列的基数。默认情况下,使用基数2.
如果范围不是给定基数的幂,则在内部使用下一个更大的幂,并且将丢弃在该范围之外生成的所有索引。
用法:
Console.WriteLine(string.Join("; ",VanDerCorputPermutation(8,2)));
// 0; 4; 2; 6; 1; 3; 5; 7
Console.WriteLine(string.Join("; ",VanDerCorputPermutation(9,2)));
// 0; 8; 4; 2; 6; 1; 3; 5; 7
Console.WriteLine(string.Join("; ",VanDerCorputPermutation(10,3)));
// 0; 9; 3; 6; 1; 2; 4; 5; 7; 8
Console.WriteLine(VanDerCorputPermutation(Int32.MaxValue,2).Count());
// 2147483647 (with constant memory usage)
foreach(int i in VanDerCorputPermutation(bigArray.Length))
{
// do stuff with bigArray[i]
}
for (int max = 0; max < 100000; max++)
{
for (int numBase = 2; numBase < 1000; numBase++)
{
var perm = VanDerCorputPermutation(max, numBase).ToList();
Debug.Assert(perm.Count==max);
Debug.Assert(perm.Distinct().Count()==max);
}
}
代码本身只使用整数arithemtic和很少的部门:
IEnumerable<int> VanDerCorputPermutation(int lessThan, int numBase = 2)
{
if (numBase < 2) throw new ArgumentException("numBase must be greater than 1");
// no index is less than zero
if (lessThan <= 0) yield break;
// always return the first element
yield return 0;
// find the smallest power-of-n that is big enough to generate all values
int power = 1;
while (power < lessThan / numBase + 1) power *= numBase;
// starting with the largest power-of-n, this loop generates all values between 0 and lessThan
// that are multiples of this power, and have not been generated before.
// Then the process is repeated for the next smaller power-of-n
while (power >= 1)
{
int modulo = 0;
for (int result = power; result < lessThan; result+=power)
{
if (result < power) break; // overflow, bigger than MaxInt
if (++modulo == numBase)
{
//we have used this result before, with a larger power
modulo = 0;
continue;
}
yield return result;
}
power /= numBase; // get the next smaller power-of-n
}
}