我有一个32位随机值(比如//Create a shading object with cell-specific coords
PdfShading shading = PdfShading.simpleAxial(w,
position.getLeft(),
position.getBottom(),
position.getRight(),
position.getTop(),
BaseColor.GRAY,
new BaseColor(255,255,255,0));
)。
631
这些位中的每一位都是一个标志。我想从这些位返回一个随机标志,如果可能的话, O(1)操作。如何从给定值{{1}中选择位位置0,1,2,4,5,6或9(或它对应的值1,2,4,16,32,64,512) }}?优选地,尽可能偏向某些位。
我提出的事情:
上面不是O(1),如果我们恰好碰到了'#39;那么可能需要多次迭代。 0位。
同样,不幸的是,上面不是O(1)。
我非常肯定这一定是可以通过某种方式进行bithisfting / masking / magic ......
编辑:
As CodeCaster suggested;这将给我所有设置位值:
0...0000001001110111
从结果数组中我可以选择一个随机元素,我已经找到了随机的'来自给定值的位(标志)。然而,这仍然需要一个(隐藏的,因为Linq)for-loop,' brute -forcing'每个可能的位,结果数组的内存分配等。
答案 0 :(得分:5)
首先,我建议你在问题中提出一个简单,直接,明显的方法:创建一个值数组,随机选择一个元素。是的,这会分配内存和诸如此类的东西。首先优化可读和正确的代码;只有当你有一个已证明的性能问题时才应优化它。
如果您确实希望将其优化到位,那么这个页面就是我的首选资源:http://graphics.stanford.edu/~seander/bithacks.html
您在此处想要的算法是:
我注意到许多算法的一个关键特性是它们是无分支的。当你试图从算法中榨取最后一盎司的性能时,请记住每一个" if"杀死表现。一个"如果"意味着缓存中的代码未运行,因为您从中分离出来,因此您更有可能发生缓存未命中。一个"如果"意味着分支预测器有机会做出错误的选择。在CLR级别,每个"如果"意味着更多的基本块,这意味着更多的工作用于抖动进行流量分析。等等。
答案 1 :(得分:2)
您可以预先创建蒙版,并选择与源值匹配的蒙版:
uint source = 631;
uint[] masks = Enumerable.Range(0, 32).Select(i => (uint)1 << i).ToArray();
uint[] matchingMask = masks.Where(m => (m & source) == m).ToArray();
现在matchingMask
包含构成source
值的值,在这种情况下为1, 2, 4, 16, 32, 64, 512
。
然后从matchingMask
开始select a random element。
如果你想要位位置,你可以像这样使用索引Select()
重载:
var matchingMask = masks.Select((m, i) => new { Index = i, Mask = m})
.Where(m => (m.Mask & source) == m.Mask)
.ToArray();
答案 2 :(得分:1)
看起来像是一个家庭作业问题......但是,解决方案是可能的,因为你只需要32位来查找,并且每个位置的时间值已经提前32位。如果我没有弄错,这些实际上是相同的(第二位设置的掩码具有值&#34; 2&#34;当被解释为整数时)。
你要做的是使用准备好的位掩码构建32个入口数组,该位掩码只返回该位。
数组查找为O(1),因为无论您要检索哪个位,速度都是常量。此时你和&amp;并且与使用位移时的原始掩码进行比较,最终结果仍为O(1)。
注意虽然这是O(1),但它可能不会比位移更快。这些数组是32 * 4字节的内存,所以128字节。这不是很大,但它也不小。您需要运行一个简单的测试来确认执行多达32位移位指令比从数组中检索项目花费更多时间(我的猜测是数组更快,但我可能错了)。
答案 3 :(得分:1)
这实际上是可能的。 There's a 64-bit solution here
我已将其转换为以下C#代码。它是O(1),因为操作数不依赖于设置的位数:
public static uint SelectRandomSetBit(ulong v, Random rng)
{
ulong a = v - ((v >> 1) & ~0UL / 3);
ulong b = (a & ~0UL / 5) + ((a >> 2) & ~0UL / 5);
ulong c = (b + (b >> 4)) & ~0UL / 0x11;
ulong d = (c + (c >> 8)) & ~0UL / 0x101;
ulong t = ((d >> 32) + (d >> 48));
int n = (int)((d * (~(ulong)0 / 255)) >> (64 - 1) * 8);
ulong r = (uint) rng.Next(1, n+1);
ulong s = 64;
s -= ((t - r) & 256) >> 3;
r -= (t & ((t - r) >> 8));
t = (d >> (int)(s - 16)) & 0xff;
s -= ((t - r) & 256) >> 4;
r -= (t & ((t - r) >> 8));
t = (c >> (int)(s - 8)) & 0xf;
s -= ((t - r) & 256) >> 5;
r -= (t & ((t - r) >> 8));
t = (b >> (int)(s - 4)) & 0x7;
s -= ((t - r) & 256) >> 6;
r -= (t & ((t - r) >> 8));
t = (a >> (int)(s - 2)) & 0x3;
s -= ((t - r) & 256) >> 7;
r -= (t & ((t - r) >> 8));
t = (v >> (int)(s - 1)) & 0x1;
s -= ((t - r) & 256) >> 8;
return (uint)(s-1);
}
以下是我测试它的方式:
Random rng = new Random();
ulong number = 0x0101010101010101;
int[] bits = new int[64];
for (int i = 0; i < 1000000; ++i)
++bits[SelectRandomSetBit(number, rng)];
for (int i = 0; i < 64; ++i)
Console.WriteLine($"bit {i} was returned {bits[i]} times.");
您可能希望看到每个第8位返回大致相同的次数,并且没有返回任何其他位。确实发生了这种情况。
我将这个转换为32位作为一项有趣的练习。 ;)
(在任何情况下,这可能是一个不必要的优化:一个简单的循环来计算位然后随机选择一个可能足够快......)
答案 4 :(得分:0)
答案 5 :(得分:0)
查找表怎么样?
public static class RandomExtensions
{
public static uint GetRandomBitOf( this Random rand, uint mask )
{
if( mask == 0 ) return 0;
var lo = smLookup[mask & 0xFFFF];
var hi = smLookup[mask >> 16];
int i = rand.Next( lo.Length + hi.Length );
return i < lo.Length ? (uint) lo[i] : (uint) hi[i - lo.Length] << 16;
}
static RandomExtensions()
{
smLookup = new ushort[65536][];
for( int i = 0; i < smLookup.Length; ++i )
{
ushort j = (ushort) i;
smLookup[i] = Enumerable
.Range( 0, 16 )
.Select( b => (ushort) ( 1 << b ) )
.Where( b => ( j & b ) != 0 )
.ToArray();
}
}
private static ushort[][] smLookup;
}
我不确定其他答案中的表现在哪里排名。我只是为了完整性而添加这个答案。