我对速度感兴趣,而不是好看的代码,这就是我使用数组而不是列表(整数)的原因。
我的数组看起来像:0,1,0,1,1,0,1,0,1,1,1,0,0,1
我对每个数字的位置很感兴趣,所以我可以随后随机选择一个。
所以我所做的是循环遍历数组以获取每个1的位置编号,然后创建一个如下所示的新数组:2,4,5,7,9,10,11,14
是否可以在这里使用?我不知道
代码看起来像:
Private Function theThing() As Integer()
Dim x As Integer
'arIn() would be a parameter
Dim arIn() As Integer = {0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1}
Dim ar() As Integer = Nothing
Dim arCount As Integer = -1
For x = 1 To arIn.GetUpperBound(0)
If arIn(x) = 1 Then
arCount += 1
End If
Next
If arCount > -1 Then
'using redim preseve is slower than the loop above
ReDim ar(arCount)
arCount = 0
For x = 1 To arIn.GetUpperBound(0)
If arIn(x) = 1 Then
ar(arCount) = x
arCount += 1
End If
Next
End If
Return ar
End Function
*编辑*
目前的解决方案(速度提高10%到15%)现在
Private Function theThing() As Integer
Dim ar() As Integer = {0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1}
Dim arLenght As Integer = ar.GetUpperBound(0)
Dim arCount As Integer = 0
Dim x As Integer
For x = 1 To arLenght
If ar(x) = 1 Then
ar(arCount) = x
arCount += 1
End If
Next
dim r As New Random()
Return ar(r.Next(arCount))
End Function
我不认为它可以比这更优化,除非有人找到一种方法来完成解决方案所做的事情,但速度更快
在这个问题出现之前,我的整个事情每10秒就可以做25500次。
现在,它可以一直超过32250,增加21%,谢谢!
答案 0 :(得分:8)
不是存储整数数组,为什么不将它们全部放入一个整数?
oldArray = [0, 1, 1, 0, 1]
newValue = 22 (binary 10110)
如果要检查是否设置了特定的位位置,请按两位与该位置的幂进行逐位比较:
is position 2 set?
value: 10110
4 (2^2): 00100
result: 00100 --> true
is position 0 set?
value: 10110
1 (2^0): 00001
result: 00000 --> false
搜索按位比较,你应该找到很多帮助。
以下是一些可能有用的Stack Overflow问题:
What are bitwise operators?
How do you set, clear, and toggle a single bit?
答案 1 :(得分:2)
关于原始算法的一些提示:
If arCount > -1
总是如此。删除它。如果您希望保持相同的输入/输出,那么我认为还有很多其他方法可以改进。
现在,如果你想要一个能够进行随机选择的函数,那么它可能会更好一些。我会用C#写,因为我知道它更好。你应该能够理解:
public static int GetRandomSetBit(int[] AllBits)
{
// Perhaps check here if AllBits is null/empty. I'll skip that for now.
int L = AllBits.Length;
int SetBitCount = 0;
// No point to save a few bytes here. Also - you can make a global array
// large enough for all occasions and skip allocating it every time.
// In that case consider automatic resizing and watch out for
// multithreading issues (if you use several threads).
int[] GoodPositions = new int[L];
for ( int i = 0; i < L; i++ )
if ( AllBits[i] != 0 )
{
GoodPositions[SetBitCount] = i;
SetBitCount++;
}
Random r = new Random(); // Should use one global instance
return GoodPositions[r.Next(SetBitCount)];
}
我担心它不会比这更好。除非你能以某种方式改变输入/输出或要求,否则不会。
答案 2 :(得分:1)
我发现很难相信redim保存会比你的循环慢,除非它本身在循环内。
在这种情况下,对于原始速度,不要只计算ar的1的数量来设置ar的大小。由于ar 永远不会大于arIn,只需将其设置为相同的大小并在末尾进行redim-preserve(不会慢,因为它在循环外部并且将始终修剪,而不是扩展 - VB希望可以就地执行此操作而不是分配更多内存。此外,如果VB每次通过循环计算它(可能是允许使用ReDim),arIn的高速缓存大小。
Private Function theThing() As Integer()
Dim x As Integer
'arIn() would be a parameter
Dim arIn() As Integer = {0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1}
Dim ar(arIn.GetUpperBound(0)) As Integer
Dim arCount As Integer
Dim arInCount As Integer
arCount = 0
arInCount = arIn.GetUpperBound(0)
For x = 1 To arInCount
If arIn(x) = 1 Then
ar(arCount) = x
arCount += 1
End If
Next
ReDim Preserve ar(arCount)
Return ar
End Function
或者,如果您略微调整返回的内容,则可以完全删除redim。使返回数组大于输入数组,并使用第一个元素控制您将随机选择的数组部分。
对于您的示例,返回的数组将是:
{8,2,4,5,7,9,10,11,14,?,?,?,?,?,?} (? values are irrelevant).
^ <-------+--------> <----+---->
| | |
| | +-- These are unused.
| |
| +-- These are used.
|
+-- This is the count of elements to use.
该代码将是:
Private Function theThing() As Integer()
Dim x As Integer
'arIn() would be a parameter
Dim arIn() As Integer = {0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1}
Dim ar(arIn.GetUpperBound(0)+1) As Integer
Dim arCount As Integer
Dim arInCount As Integer
arCount = 0
arInCount = arIn.GetUpperBound(0)
For x = 1 To arInCount
If arIn(x) = 1 Then
ar(arCount) = x
arCount += 1
End If
Next
ar(0) = arCount
Return ar
End Function
然后,在您的代码中从ar中选择一个随机值,而不是:
rndval = rnd(ar.GetUpperBound)
使用:
rndval = rnd(ar(0) + 1)
答案 3 :(得分:1)
禁用溢出检查。
项目属性 - &gt;编译 - &gt;高级编译选项 - &gt;删除整数溢出检查
如果需要对项目的其余部分进行溢出检查,可以将代码移动到新项目(例如DLL)并禁用该新项目的溢出检查。
还要确保您正在运行发布版本(已启用优化)并且您没有对其进行调试。
编辑:我得到8.5秒(12秒,如果我在For I'm用于测试中声明数组)调用该函数50毫秒。如果您只获得32000,则使用非常大的输入或某些东西会降低代码速度。例如,如果您计算程序内部的时间并且在分析器中运行它,则会得到错误的结果,因为分析可能会大大减慢程序的速度。像Slow methods calls这样的故障可能会影响性能。
答案 4 :(得分:1)
我认为当Recursive引用BitArray时,他的意思是这样的:
using System.Collections.Generic;
using System;
using System.Collections;
class Program
{
static void Main(string[] args)
{
var input = new BitArray(new byte[] { 0x5A /*01011010b*/
, 0xE4 /*11100100b*/ });
var output = new List<int>();
var offset = 1;
foreach (var bit in input)
{
if (bit)
{
output.Add(offset);
}
offset++;
}
}
}
答案 5 :(得分:0)
速度有多少项?
更改项目?
编译时间或执行时间?
有什么空间限制?
一个已知的策略是将几个组合在一起并写出所有组合及其结果: 0000 - &gt; 0001 - &gt; 4 0010 - &gt; 3 0011 - &gt; 3,4 0100 - &gt; 2 0101 - &gt; 2,4 0110 - &gt; 2,3 ...
为什么要从这个二进制表示转换为随机选择一位?这不太可能有助于提高绩效。最好将它们分组为8位,使用表格告诉你组中有多少1,并重复5次。那么你知道有多少个。随机做一遍,然后查找所选的那个。
答案 6 :(得分:0)
您可能会发现使用For Each并且初始化为至少输入数组长度的列表比索引器更快:
If arIn.Length > 0 Then
Dim lstOut As New List(Of Integer)(arIn.Length)
Dim ix As Integer = 0
For Each val As Integer In arIn
If val = 1 Then
lstOut.Add(ix)
End If
ix = ix + 1
End If
Return lstOut.ToArray()
Else
Return Nothing
End If
但你必须测试它。
答案 7 :(得分:0)
这就是BitArray的目的。
答案 8 :(得分:0)
也许您可以使用转换表来挤压最高性能。然后应用下面的算法:
抱歉,我不再熟悉VB,所以我必须写C#。这是整个代码的一部分,因为整个lookupTable将有256个项目。
using System.Collections.Generic;
class Program
{
static void Main(string[] args)
{
var input = new byte[] { 0x5A /*01011010b*/, 0xE4 /*11100100b*/ };
var output = new List<int>();
var lookupTable = new int[][] { new int[0] /*00000000b*/
, new int[] { 8 } /*00000001b*/
, new int[] { 7 } /*00000010b*/
, new int[] { 7,8 } /*00000011b*/
, new int[] { 6 } /*00000100b*/
, new int[] { 6,8 } /*00000101b*/
, new int[] { 6,7,8 } /*00000111b*/
};
int offset = 0;
foreach (var value in input)
{
foreach (var position in lookupTable[(int)value])
{
output.Add(position + offset);
}
offset += 8;
}
}
}
答案 9 :(得分:0)
如果很容易生成一个略大于阵列长度的素数(取决于它的大小,这可能也可能不容易),而你并不关心完全均匀随机,那么你可以生成该范围内的随机数并生成该循环。它应该在几次迭代中找到答案(基于1的密度)。 c#中的源代码,因为我不记得vb语法:
int RandomInArray(int[] ar)
{
int p = GenerateSlightlyLargerPrime(ar.Length);
int x = random.nextInt(p);
int i = x;
do
{
if (i < ar.Length && ar[i] == 1)
return i;
i = (i + x) % p;
} while (i != x);
return -1;
}
请注意,这不是100%均匀随机,但它应该非常接近。
答案 10 :(得分:0)
我尝试过不同的方法。我们的想法是在找到对应于1的条目时继续进行随机输入和退出。这种解决方案并不完美,因为没有使用某些可能会或可能不会破坏随机性的random。另外,速度很大程度上取决于“1”的密度。代码如下:
public static int GetRandomSetBit(int[] AllBits)
{
int L = AllBits.Length;
Random r = new Random(); // Better use a global instance as this hurt performance a lot
do
{
selected = r.Next(L);
} while (AllBits[selected] == 0);
return selected;
}
在我的电脑中,如果将Random对象的创建移动到全局,如果30中有5“1”,它可以在11s左右运行50000000次试验。如果有最多15“1”,所需的时间缩短到5秒左右。
与Vilx建议的代码相比,他的代码可以在13秒左右在我的电脑上运行50000000次试验