我需要一个整数值函数,它返回一个递增序列,直到达到给定的最大值,然后再次下降。我称之为Ziggurat:
0 1 2 3 4 5 6 6 5 4 3 2 1 0
步骤一个接一个。最大值(在序列中间)必须出现两次。在0到2 * max范围之外,我不在乎会发生什么。
我希望功能快 - 没有分支。我更喜欢按位操作。
作为一个不起作用的例子,这是我的金字塔函数,以及我对绝对值的实现:
private static readonly int LONG_ABS_MASK_SHIFT = sizeof(long) * 8 - 1;
/// <summary>
/// Compute the Absolute value of a long without branching.
///
/// Note: This will deviate from Math.Abs for Int64.MinValue, where the .NET library would throw an exception.
/// The most negative number cannot be made positive.
/// </summary>
/// <param name="v">Number to transform.</param>
/// <returns>Absolute value of v.</returns>
public static long Abs(this long v)
{
long mask = v >> LONG_ABS_MASK_SHIFT;
return (v ^ mask) - mask;
}
public static long Pyramid(this long N, long max)
{
return max - (max - N).Abs();
}
此金字塔函数创建的序列如0 1 2 3 4 5 6 5 4 3 2 1 0
请注意,中间数字只出现一次。
我有一个想法是将一个查找表存储为long或BigInteger中的连续位块,并将它们移位并屏蔽掉它们,但是对于长序列来说,这会占用太多内存。但它只使用很少的指令。
答案 0 :(得分:3)
试试这个:
public static long Pyramid2(this long N, long max)
{
return N.Pyramid(max + 1) + ((max - N) >> -1);
}
结果:
0 1 2 3 4 五 6 6 五 4 3 2 1 0
结果如下:
\ N=0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
N.Pyramid(6 + 1) 0 1 2 3 4 5 6 7 6 5 4 3 2 1 0 -1 -2 -3 -4
+ ((max - N) >> -1) 0 0 0 0 0 0 0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
= 0 1 2 3 4 5 6 6 5 4 3 2 1 0 -1 -2 -3 -4 -5
Pyramid 是问题中定义的原始 Pyramid 方法。
答案 1 :(得分:2)
功能:
public static long Ziggurat(this long N, long max)
{
N = max - N;
return max - (N ^ N >> -1);
}
没有循环,测试或分支。只需要四次操作;两个是按位的。
但是,您的问题存在问题。您指定输出:
0 1 2 3 4 5 6 6 5 4 3 2 1 0
此序列有14个元素。但是,您还将函数的范围指定为“0到2 * max”,其中只有13个元素!我假设范围为零到2 * max + 1
是可以接受的。
而且,这是一个基于控制台的测试:
static void Main(string[] args)
{
long max = 6;
for (long i = 0; i <= 2 * max + 1; i++)
{
Console.Write("{0} ", i.Ziggurat(max));
}
}
输出:
0 1 2 3 4 5 6 6 5 4 3 2 1 0
行N = max - N
会使输入值从数字行的最大值开始向下计数,以使范围0 through 13
变为6 through -7
。
请注意以下事项:
-1
的按位补码为0
(~11111111 = 00000000)。-7
的按位补码为6
(~11111001 = 00000110)。因此,如果我们从6
计算到-7
并补充所有负值,我们会得到包含两个零的序列:
6 5 4 3 2 1 0 0 1 2 3 4 5 6
第二行中的子表达式N ^ N >> -1
补充N
,但仅当N
为负数时才会补充。
第二行max - …
中表达式的剩余部分将上面的序列反转为所需的输出:
0 1 2 3 4 5 6 6 5 4 3 2 1 0
∎
答案 2 :(得分:1)
public static IEnumerable<long> getPyramid(long maxValue)
{
for(long i = 0; i <= maxValue; i++)
{
yield return i;
}
for(long i = maxValue; i >=0; i--)
{
yield return i;
}
}
有人可能会用Concat
Enumerable.Range
以及可能的选择/反向或类似的东西处理整个事情,但它可能会稍微低效,因为我不知道琐碎让它倒计时的方式。反向将比循环的屈服更“工作”,并且select(执行maxvalue减去Enumerable.Range
的当前迭代)将进行一堆额外的算术,所有这些都是为了避免几行代码。
即:
public static IEnumerable<long getPyramid(long maxValue)
{
return Enumerable.Range(0, maxValue)
.Concat(Enumerable.Range(0, maxValue).Select(num => maxValue - num));
}