我试图在字节上执行非线性函数来实现SAFER +。该算法需要计算基数为45的字节对数,我不明白该怎么做。
log 45 (201)= 1.39316393
当我将其分配给一个字节时,该值被截断为1,我无法恢复确切的结果。
我该怎么办呢?
答案 0 :(得分:4)
在这种情况下,密码术经常使用prime fields,,GF(257)。 Create an exponentiation table看起来像这样:
exp | log ----+---- 0 | 1 1 | 45 2 | 226 3 | 147 ... | ... 128 | 0 ... | ... 255 | 40 ---------
“log”值为45 exp %257。您需要一个具有modPow
函数的任意精度算术库(将数字提升为幂,以某个值为模)建立这个表。您可以看到“exp”128的值是一种特殊情况,因为通常零的对数是未定义的。
通过在“log”列中找到它来计算数字的对数;该行的“exp”列中的值是对数。
这是初始化草图:
BigInteger V45 = new BigInteger(45);
BigInteger V257 = new BigInteger(257);
byte[] exp = new byte[256];
for (int idx = 0; idx < 256; ++idx)
exp[idx] = BigInteger.ModPow(V45, new BigInteger(idx), V257) % 256;
byte[] log = new byte[256];
for (int idx = 0; idx < 256; ++idx)
log[exp[idx]] = idx;
通过此设置,例如,log 45 (131)= log[131]
= 63,并且45 38 = exp[38]
= 59。
(我从未写过C#;我只是在BigInteger
文档中猜测;数据类型可能存在错误。)
答案 1 :(得分:1)
所以你有一个字节值(从0到255),你想获得日志基数45,并将其存储在另一个字节中?正如其他人所说,你这样做会失去一些准确性。但是,您可以做的不仅仅是将double
结果转换为byte
。
255的对数基数45约为1.455675。您可以通过将其乘以常数因子将其存储在一个字节中,但精度会有所降低。什么常数因素?你可以使用100,它会给你145的值,但你几乎失去了一个字节的一半范围。由于您要表示的最大值为1.455675,因此您可以使用255/log45(255)
的常数乘数,或约175.176。
这有多好?我们看看......
var mult = 255.0 / Math.Log(255, 45);
Console.WriteLine("Scaling factor is {0}", mult);
double errMax = double.MinValue;
double errMin = double.MaxValue;
double errTot = 0;
for (int i = 1; i < 256; ++i)
{
// Get the log of the number you want
var l = Math.Log(i, 45);
// Convert to byte
var b = (byte)(l * mult);
// Now go back the other way.
var a = Math.Pow(45, (double)b / mult);
var err = (double)(i - a) / i;
errTot += err;
errMax = Math.Max(errMax, err);
errMin = Math.Min(errMin, err);
Console.WriteLine("{0,3:N0}, {1,3:N0}, {2}, {3:P4}", i, b, a, err);
}
Console.WriteLine("max error = {0:P4}", errMax);
Console.WriteLine("min error = {0:P4}", errMin);
Console.WriteLine("avg error = {0:P4}", errTot / 255);
在我的机器上的.NET 4下,最大误差为2.1419%,平均误差为1.0501%。
您可以通过舍入Math.Pow
的结果来减少平均错误。那就是:
var a = Math.Round(Math.Pow(45, (double)b / mult));
将平均误差降低到0.9300%,但将最大误差增加到3.8462%。
答案 2 :(得分:0)
向我们展示代码可能有所帮助,但我怀疑您的问题来自存储结果。
如果你想存储一个非整数,你不想把它放到一个字节中,因为那会截断它(如你所见)。而是将结果存储为双精度或更合适的结果:
double result = math.log(154,45);
我应该补充一点,我不确定SAFER +是什么,所以这个答案可能没有帮助,但希望它能指出你正确的方向。
答案 3 :(得分:0)
这不是一个真正的答案,但是在查看此问题的用户中,有一部分可能会对将double
类型转换为byte[]
类型感兴趣。
可以做的只是:
double theDouble = 78.24435;
byte[] theResult = BitConverter.GetBytes(theDouble);
和
byte[] theByteArray = new byte[]{0, 4, 2, 3}; //for example
double theCorrespondingDouble = BitConverter.ToDouble(theByteArray);
这使用我认为最初存在于.NET中的BitConverter
类。