一个性能稍微做作的方式可能是这(伪代码):
- 构建位掩码为你介绍,在此基础上的邻居是开放的。
使用位掩码为索引的数组:
struct RandomData
{
size_t num_directions;
struct { signed int dx, dy; } deltas[4];
} random_data[16];
其中num_directions是打开邻居的数量,以及deltas[]
告诉你怎么去每个邻居。
这有很多繁琐的数据,但它消除了循环和分支。
更新:好吧,出于某种原因,我有问题让这个想法去。我在工作中责备关于“数据驱动编程”的一定程度的灌输,因为这个非常简单的问题使我“认识到”数据驱动性更好一些。这总是很好。
不管怎么说,这是一个完整的,经过测试和使用工作实施随机步进功能的上述思路:
/* Directions are ordered from north and going clockwise, and assigned to bits:
*
* 3 2 1 0
* WEST | SOUTH | EAST | NORTH
* 8 4 2 1
*/
static void random_walk(unsigned int *px, unsigned int *py, unsigned max_x, unsigned int max_y)
{
const unsigned int x = *px, y = *py;
const unsigned int dirs = ((x > 0) << 3) | ((y < max_y) << 2) |
((x < max_x) << 1) | (y > 0);
static const struct
{
size_t num_dirs;
struct { int dx, dy; } deltas[4];
} step_info[] = {
#define STEP_NORTH { 0, -1 }
#define STEP_EAST { 1, 0 }
#define STEP_SOUTH { 0, 1 }
#define STEP_WEST { -1, 0 }
{ 0 },
{ 1, { STEP_NORTH } },
{ 1, { STEP_EAST } },
{ 2, { STEP_NORTH, STEP_EAST } },
{ 1, { STEP_SOUTH } },
{ 2, { STEP_NORTH, STEP_SOUTH } },
{ 2, { STEP_EAST, STEP_SOUTH } },
{ 3, { STEP_NORTH, STEP_EAST, STEP_SOUTH } },
{ 1, { STEP_WEST } },
{ 2, { STEP_NORTH, STEP_WEST } },
{ 2, { STEP_EAST, STEP_WEST } },
{ 3, { STEP_NORTH, STEP_EAST, STEP_WEST } },
{ 2, { STEP_SOUTH, STEP_WEST } },
{ 3, { STEP_NORTH, STEP_SOUTH, STEP_WEST } },
{ 3, { STEP_EAST, STEP_SOUTH, STEP_WEST } },
{ 4, { STEP_NORTH, STEP_EAST, STEP_SOUTH, STEP_WEST } }
};
const unsigned int step = rand() % step_info[dirs].num_dirs;
*px = x + step_info[dirs].deltas[step].dx;
*py = y + step_info[dirs].deltas[step].dy;
}
int main(void)
{
unsigned int w = 16, h = 16, x = w/2, y = h/2, i;
struct timeval t1, t2;
double seconds;
srand(time(NULL));
gettimeofday(&t1, NULL);
for(i = 0; i < 100000000; i++)
{
random_walk(&x, &y, w - 1, h - 1);
}
gettimeofday(&t2, NULL);
seconds = (t2.tv_sec - t1.tv_sec) + 1e-6 * (t2.tv_usec - t1.tv_usec);
printf("Took %u steps, final position is (%u,%u) after %.2g seconds => %.1f Msteps/second\n", i, x, y, seconds, (i/1e6)/seconds);
return EXIT_SUCCESS;
}
一些解释可能是为了,上面是非常不透明的,直到你“搞定”它,我猜:
- 函数本身的接口应该是清楚的。请注意,网格的宽度和高度表示为“
max_x
”和“max_y
”,以便在检查当前位置是否在边框上时保存一些常量减法。
- 将变量
dirs
设置为“打开”方向的位掩码。对于空网格,除非您位于边框上,否则这总是为0x0f。当然,这可以通过测试地图来处理墙壁。
step_info
阵列收集有关哪些步骤可以从16种可能的开放方向组合中的每一种获取的信息。当读取每个struct
的初始化值(每行一个)时,请考虑二进制的结构索引,并将其转换为dirs
中的位。
STEP_NORTH
宏(和朋友)减少了打字,并使其更清晰地发生了什么。
- 我喜欢
random_walk()
的“肉”如何只是四个几乎清晰的表达式,它令人耳目一新,看不到一个单一的if
。
在我的2.4 GHz x86_64系统上使用gcc(Ubuntu/Linaro 4.4.4-14ubuntu5)4.4.5进行编译时,使用优化级别-O3,性能似乎仅为每秒3600万步。阅读程序集的核心逻辑是无分支的。当然有一个rand()
的电话,我不想一路走下来,并实施一个本地随机数发生器已经内联。
注意:这并没有解决问的确切问题,但我觉得这项技术值得扩展。
感谢您的快速回答:) – 2011-01-21 13:29:03
从技术上讲,随机方向方法可能需要无限次数的尝试,只有一个有效的方向....你可能非常不走运! – mikera 2011-01-21 13:31:24
是的,我同意它可以采取无限数量的尝试(只要有任何无效的方向,它就可以),但平均数量为四,尝试次数不可能非常高。 – 2011-01-21 13:33:03