我有一个字节数组和该数组的长度。目标是输出包含该数字的字符串,表示为基数为10的数字。
我的阵列是小端。这意味着第一个(arr[0]
)字节是最低有效字节。这是一个例子:
#include <iostream>
using namespace std;
typedef unsigned char Byte;
int main(){
int len = 5;
Byte *arr = new Byte[5];
int i = 0;
arr[i++] = 0x12;
arr[i++] = 0x34;
arr[i++] = 0x56;
arr[i++] = 0x78;
arr[i++] = 0x9A;
cout << hexToDec(arr, len) << endl;
}
该数组由[0x12, 0x34, 0x56, 0x78, 0x9A]
组成。我想要实现的函数hexToDec
应返回663443878930
,这是十进制数。
但是,问题是因为我的机器 32位所以它输出2018915346
(注意这个数字来自整数溢出)。所以,问题是因为我使用天真的方式(迭代数组并将其乘以256
到数组中位置的幂,然后乘以该位置的字节,最后加到总和)。这当然会产生整数溢出。
我也试过了long long int
,但当然在某些时候会发生整数溢出。
我希望表示为十进制数的数组可以很长(超过1000个字节),definitelly需要比我天真的算法更聪明的算法。
问题
实现这一目标的好算法是什么?另外,我必须问的另一个问题是该算法的最佳复杂性是什么?可以用线性复杂度O(n)
来完成,其中n
是数组的长度吗?我真的想不出一个好主意。实施不是问题,我缺乏想法。
建议或想法如何做到这一点就足够了。但是,如果使用某些代码更容易解释,请随意用C ++编写。
答案 0 :(得分:0)
您可以而且无法在O(n)
中实现此目的。一切都取决于您的号码的内部表示。
对于真正的二进制形式(2个基数的功率,如256)
这在O(n)
中无法解决这个数字的十六进制打印在O(n)
但是您可以将 HEX 字符串转换为decadic并返回如下:
由于创建十六进制字符串不需要bignum数学。您只需将阵列从 MSW 打印到 HEX 中的 LSW 。这是O(n)
,但转换为 DEC 不是。
要以十进制形式打印bigint,您需要连续对其进行mod / div 10,从 LSD 到 MSD 获取数字,直到subresult为零。然后只需按相反的顺序打印它们......除法和模数可以一次完成,因为它们是相同的操作。因此,如果您的号码有N
十进制数字,那么您需要N
bigint分区。每个bigint分区都可以通过二进制除法完成,因此我们需要log2(n)
位移和减法,这些都是O(n)
所以原生bigint
打印的复杂性是:
O(N.n.log2(n))
我们可以用N
计算n
的对数,以便BYTE
s:
N = log10(base^n)
= log10(2^(8.n))
= log2(2^(8.n))/log2(10)
= 8.n/log2(10)
= 8.n*0.30102999
= 2.40824.n
所以复杂性将是:
O(2.40824.n.n.log2(n)) = O(n^2.log2(n))
对于真正的大数字而言,这是绝对的。
10基本二进制形式的力量
要在O(n)
中执行此操作,您需要稍微更改数字的基数。它仍将以二进制形式表示,但基数为10的幂。
例如,如果您的号码将由16bit WORDs
表示,则您可以使用仍然适合其中的最高基数10000
(最大值为16536
)。现在您可以轻松地以十进制方式打印,因此只需打印阵列中的每个单词,从 MSW 到 LSW 。
示例:
允许将大号1234567890
存储为BYTEs
,基数为100
,其中 MSW 排在第一位。所以号码将按如下方式存储
BYTE x[] = { 12, 34, 56, 78, 90 }
但正如您在使用BYTEs
和基座100
时所看到的那样,我们正在浪费空间,因为在100*100/256=~39%
范围内只使用了BYTE
。这些数字的操作与原始二进制形式略有不同,因为我们需要以不同的方式处理上溢/下溢和携带标志。
BCD(二进制编码的十进制)
还有另一个选项是使用 BCD (二进制编码的十进制),它几乎与之前的选项相同,但基数10用于数字的单个数字...每个nibel( 4位)恰好包含一位数。处理器通常具有该数字表示的指令集。用法类似于二进制编码的数字,但在每次算术运算之后都是 BCD 恢复指令,称为DAA
,它使用Carry和Auxiliary Carry标志状态来恢复 BCD 编码结果。要以十进制格式打印 BCD 中的值,您只需将值打印为 HEX 。我们之前的例子中的数字将用BCD编码,如下所示:
BYTE x[] = { 0x12, 0x34, 0x56, 0x78, 0x90 }
当然,#2,#3 无法在O(n)
打印您的号码 HEX 。
答案 1 :(得分:0)
你需要提高你的小学技能并实施long division。
我认为你最好在基数16中实现长除法(每次迭代将数字除以0x0A)。记下每个部门的提示 - 这些是您的十进制数字(第一个是最低有效数字)。
答案 2 :(得分:0)
您发布的0x9a78563412
号码,正如您以小端格式表示的那样,可以使用以下代码转换为正确的uint64_t
:
#include <iostream>
#include <stdint.h>
int main()
{
uint64_t my_number = 0;
const int base = 0x100; /* base 256 */
uint8_t array[] = { 0x12, 0x34, 0x56, 0x78, 0x9a };
/* go from right to left, as it is little endian */
for (int i = sizeof array; i > 0;) {
my_number *= base;
my_number += array[--i];
}
std::cout << my_number << std::endl; /* conversion uses 10 base by default */
}
示例运行给出:
$ num
663443878930
因为我们在2的精确幂的基础上,我们可以使用
来优化代码my_number <<= 8; /* left shift by 8 */
my_number |= array[--i]; /* bit or */
因为这些操作比整数乘法和求和更简单,所以预期这样做会有一些(但不是很多)效率提高。将它留在第一个例子中应该更具表现力,因为它更像是一个任意的基本转换。