能否高速转换
我有一个数组:Aarry of Byte.
中间存放的都是0,1两种状态。
能否有一种最快的速度转换为16进制
举例说明如下:
数组现在数据如下(数据中有44个值):
1111 1101 0000 0101 1010 0101 0110 1010 0000 1000 0111
则结果为:FC09A96A087
问题点数:20、回复次数:50Top
1 楼zhangcheng125(老狼)回复于 2006-12-26 21:18:33 得分 0
可以用BinToHex函数Top
2 楼lihuasoft(坐井观天)回复于 2006-12-26 23:10:32 得分 5
按我对题目的理解,楼主提供的数组有 44 个元素,每个元素的值非1即0(即仅利用了一字节中的一位),对吗?
如果我对题目的理解是正确的,那么请楼主往下看我的代码(稍候贴出);如果我对题目理解不正确,即,楼主的数组仅有11个元素,那么楼主直接跳过我的代码,直接用Format('%x',[8位元素])函数输出即可。Top
3 楼lihuasoft(坐井观天)回复于 2006-12-26 23:18:23 得分 0
以下是我的代码:
{ 方案一 }
procedure TForm1.Button1Click(Sender: TObject);
var
Arr1 : array [1..44] of byte;//这是源数组(每元素8位仅用1位),如需改为动态,请自己改
Arr2 : array of byte; //这是存放结果的数组
I, J, X : integer;
begin
FillChar(Arr1,44,0); //---------------------
For I := Low(Arr1) to High(Arr1) do //| 这部分是按楼主题目|
if I in [1,2,3,4,5,6,8,14,16,17,19,22,24,//|中提供的数据对源数组|
26,27,29,31,37,42,43,44] then //|进行了赋初值 |
Arr1[I] := 1; //---------------------
{此时源数组44个元素值为11111101000001011010010101101010000010000111}
{ 给结果数组定长。为了以后代码通用而这样处理。本例中44 div 4 == 11 }
SetLength(Arr2, Length(Arr1) div 4);
X := Low(Arr1); //取源数组的下标下限,在本例中为1 。也是为了以后通用。
{ 以下是实现赋值 }
for I := Low(Arr2) to High(Arr2) do
begin
Arr2[I] := 0 ;
for J :=0 to 3 do
Arr2[I] := Arr2[I] + (Arr1[X+J] shl (3-J)) and $F ;
Inc(X,4);
end;
{ 以下是在一个Memo中显示结果数组的各元素值 }
for I := Low(Arr2) to High(Arr2) do
begin
memo1.Text := memo1.Text + Format('%x',[Arr2[I]]);
end;
{ 结果显示为:FD05A56A087 楼主的结果是错误的 }
end;
//代码看起来有点长,是因为其中包含了注释和“赋初值”、“显示结果”等等附加代码。
//其实,这个方案的核心思想很简单,就是把源数组每4个元素值放在了结果数组中1个元素里
Top
4 楼lihuasoft(坐井观天)回复于 2006-12-26 23:32:03 得分 0
{ 方案二 }
用IntToStr转换,然后四个元素字符串相接,转换为一个16进制数......这种方案,没时间搞了。
{ 声明:上面我给出的两种方案,可能都不够快,都达不到楼主的要求。望谅。 }Top
5 楼maozefa(阿发伯)回复于 2006-12-27 09:01:52 得分 0
Arr1 为楼主的数组
var
I: Byte;
S: string;
begin
S := '';
for I := Low(Arr1) to High(Arr1) do
S := S + IntToHex(I, 2);
end;
Top
6 楼wywry(Wyatt)回复于 2006-12-27 09:02:09 得分 0
//首选衷心感谢:lihuasoft
//回去,我想到一个方法,如下:
proceudre TForm1.Button1Client(Sender:TObject);
const
Arr1:Array[0..3] of Integer=(8,4,2,1);
var
s:String;
i,w_code:Integer;
ar:Array of Byte;
begin
s:='';//存放结果
w_code:=0; //存放临时转换的字符
SetLength(Ar,1024);
Randomize;
for i:=Low(Ar) to High(Ar) do //生成测试数据
Ar[i]:=Random(2);
for I := Low(Ar) to (High(Ar)) do
begin
w_code:=w_code + Ar[i]*Arr1[i mod 4];
if ((i+1) mod 4)=0 then
begin
s:=s+IntToHex(w_code,1);
w_code:=0;
end;
end;
Memo1.Text:=s;
end;
//以上结果是正确,并且速度也比较快,不知大家还有没有更为巧妙的算法Top
7 楼wywry(Wyatt)回复于 2006-12-27 09:04:22 得分 0
To:maozefa(阿发伯)
S := S + IntToHex(I, 2); //你转换的是I
我想把数组中数据转换,你看看我上面的代码可能就明白我的意思Top
8 楼maozefa(阿发伯)回复于 2006-12-27 09:04:44 得分 0
上面不符合楼主数据的要求,改为:
var
I: Byte;
S: string;
begin
S := '';
for I := Low(Arr1) to High(Arr1) do
S := S + IntToHex(I, 1);
end;
Top
9 楼maozefa(阿发伯)回复于 2006-12-27 09:07:09 得分 0
我想把数组中数据转换,你看看我上面的代码可能就明白我的意思
=================================================================
我转换的是数组中的数据啊,I只是个循环过渡变量。Top
10 楼wywry(Wyatt)回复于 2006-12-27 09:28:39 得分 0
{
:)我测试过,不行
能否帮我讲解一下,你的代码意义
}
var
I: Byte;
S: string;
begin
S := '';
for I := Low(Arr1) to High(Arr1) do //从Arr1下标开始循环,假如从0开始,100结束
S := S + IntToHex(I, 1); //转换为16进制,循环累加。
//此时转换的是I值,还是Arr1[I]的值.
end;Top
11 楼maozefa(阿发伯)回复于 2006-12-27 09:32:30 得分 0
我的循环依次取Arr1数组的值,转换为1字节的16进制字符串,加起来,不就是你要的结果吗?Top
12 楼maozefa(阿发伯)回复于 2006-12-27 09:33:47 得分 0
此时转换的是I值,还是Arr1[I]的值
============================================================
相当于Arr1[I]Top
13 楼maozefa(阿发伯)回复于 2006-12-27 09:42:06 得分 0
我搞错了,哈哈, 多亏楼主提醒,应该是下面这样:
const
Arr1: array[0..10] of byte = (15, 13, 0, 5, 10, 5, 6, 10, 0, 8, 7);
var
I: Byte;
S: string;
begin
S := '';
for I := Low(Arr1) to High(Arr1) do
S := S + IntToHex(Arr1[I], 1);
ShowMessage(s);
end;
Top
14 楼wywry(Wyatt)回复于 2006-12-27 09:43:33 得分 0
呵,你的理解有误:
我是四个四个一组,换成一个16进制,不是每一个都转换
在数组中存放都是0与1
如:111110011000 则转换成F98
另I相当于Arr1[I]真的不是很明白?Top
15 楼maozefa(阿发伯)回复于 2006-12-27 09:46:19 得分 5
数组有44个元素?那你按lihuasoft(学习低调做人) 的方法去做。Top
16 楼lihuasoft(坐井观天)回复于 2006-12-27 09:52:28 得分 0
嗯, 楼主的意思是源数组里每个元素的值非1即0,即只用低1位.
所以楼主要探讨的实际上是这样一个问题 : 怎样把上述数组值转存到一个紧凑的新数组里去
即,如何得到这样一个数组:Arr2 : array[0..10] of byte = (15, 13, 0, 5, 10, 5, 6, 10, 0, 8, 7);
如果要输出它的值,直接用
for I := Low(Arr2) to High(Arr2) do
memo1.Text := memo1.Text + Format('%x',[Arr2[I]]);
就可以了
Top
17 楼maozefa(阿发伯)回复于 2006-12-27 09:57:52 得分 0
to lihuasoft(学习低调做人)
看来还是你的理解能力强!人老了,记忆不好,理解能力差,代码也写错,哈哈。
Top
18 楼lihuasoft(坐井观天)回复于 2006-12-27 10:05:11 得分 0
to 阿发伯:
大哥你谦虚了. 我在开始时也是没有搞懂楼主的意思. 可能楼主的源数据有特殊要求,只能那样存放, 的缘故吧.
新年快到了, 顺便祝大哥新年财运亨通! (灌上一两层的水,楼主不要生气啊!)Top
19 楼wywry(Wyatt)回复于 2006-12-27 10:18:27 得分 0
呵,是的,我是用做将图片转换为字模使用的。
谢谢大家。
祝:
猪年好运!Top
20 楼maozefa(阿发伯)回复于 2006-12-27 10:46:57 得分 0
谢谢各位祝福!另外,从转换效率看,我觉得lihuasoft(学习低调做人)的转换方法比楼主的快,因为转换过程中没使用乘除法。Top
21 楼maozefa(阿发伯)回复于 2006-12-27 10:52:29 得分 0
但是,lihuasoft(学习低调做人)的
SetLength(Arr2, Length(Arr1) div 4);
不通用,如果Length(Arr1)不是4的整倍数呢?当然楼主的例子应该是4的整倍数,可改为:
SetLength(Arr2, (Length(Arr1) + 3) div 4);
以上,只是就事论事,无事闲聊罢了,各位不要见怪!Top
22 楼lihuasoft(坐井观天)回复于 2006-12-27 11:14:39 得分 0
嗯, 的确是的.Top
23 楼lihuasoft(坐井观天)回复于 2006-12-27 11:25:00 得分 0
晕----我怎么就没想到 (8+3) Div 4 仍然等于 8 Div 4 呢! 昨天晚上为这个就晕了好一阵子
~ ~
OTop
24 楼maozefa(阿发伯)回复于 2006-12-27 11:30:42 得分 0
哈哈,这就是所谓的聪明一世,糊涂一时,你看我上面,非说I = Low(Arr1)就是Arr1[I],晕死,特别是解答问题时,考虑不周是正常的。
Top
25 楼jrcn(梦里寻软)回复于 2006-12-27 12:13:05 得分 0
markTop
26 楼zswang(伴水清清)(专家门诊清洁工)回复于 2006-12-27 12:19:42 得分 10
procedure TForm1.Button1Click(Sender: TObject);
const
Arr1: array[0..3] of Integer = (8, 4, 2, 1);
cHexChars: array[0..15] of Char = '0123456789ABCDEF';
var
s: string;
I, w_code: Integer;
ar: array of Byte;
vTickCount: Longword; // 计时
begin
s := ''; //存放结果
w_code := 0; //存放临时转换的字符
SetLength(Ar, 1024000); // 测试数据开大一些
// 4个二进制换一个十六进制 //S[I div 4 + 1] := cHexChars[w_code];一起打开
SetLength(S, Length(Ar) div 4);
//Randomize;//先确定结果是一致的
for I := Low(Ar) to High(Ar) do //生成测试数据
Ar[I] := Random(2);
vTickCount := GetTickCount;
for I := Low(Ar) to High(Ar) do
begin
w_code := w_code + Ar[I] * Arr1[I mod 4];
if (I + 1) mod 4 = 0 then
begin
// S := S + IntToHex(w_code, 1); // 172毫秒
// S := S + cHexChars[w_code]; // 125毫秒
S[I div 4 + 1] := cHexChars[w_code]; // 16毫秒 // 关键
w_code := 0;
end;
end;
Caption := IntToStr(GetTickCount - vTickCount); // 输出处理时间
Memo1.Text := Copy(S, 1, 1024); // 输出部分结果
end;
百万级数据量:从172毫秒优化到16毫秒 -_-!!!!!Top
27 楼maozefa(阿发伯)回复于 2006-12-27 12:27:34 得分 0
S[I div 4 + 1] := cHexChars[w_code]; // 16毫秒 // 关键
改为
S[I shr 2 + 1] := cHexChars[w_code]; // 能否还可缩短?Top
28 楼maozefa(阿发伯)回复于 2006-12-27 13:16:18 得分 0
理论上,乘除慢于加减,加减慢于位运算,我在zswang(伴水清清)基础上进一步优化,百万级数据仅仅缩短1毫秒!!!
procedure TForm1.Button2Click(Sender: TObject);
const
// Arr1: array[0..3] of Integer = (8, 4, 2, 1);
cHexChars: array[0..15] of Char = '0123456789ABCDEF';
var
s: string;
I, J, K, w_code: Integer;
ar: array of Byte;
vTickCount: Longword; // 计时
begin
s := ''; //存放结果
w_code := 0; //存放临时转换的字符
SetLength(Ar, 1024000); // 测试数据开大一些
// 4个二进制换一个十六进制 //S[I div 4 + 1] := cHexChars[w_code];一起打开
SetLength(S, Length(Ar) div 4);
//Randomize;//先确定结果是一致的
for I := Low(Ar) to High(Ar) do //生成测试数据
Ar[I] := Random(2);
J := 1;
vTickCount := GetTickCount;
for I := Low(Ar) to High(Ar) do
begin
// w_code := w_code + Ar[I] * Arr1[I and 3];
// if (I + 1) and 3 = 0 then
// begin
// S := S + IntToHex(w_code, 1); // 172毫秒
// S := S + cHexChars[w_code]; // 125毫秒
// S[I div 4 + 1] := cHexChars[w_code]; // 16毫秒 // 关键
// end;
K := (I - Low(Ar)) and 3;
w_code := w_code or Ar[I] shl (3 - K);
if K = 3 then
begin
S[J] := cHexChars[w_code];
w_code := 0;
Inc(J);
end;
end;
Caption := IntToStr(GetTickCount - vTickCount); // 输出处理时间
Memo1.Text := Copy(S, 1, 1024); // 输出部分结果
end;
Top
29 楼zswang(伴水清清)(专家门诊清洁工)回复于 2006-12-27 13:36:55 得分 0
楼上只能说明你的机器比我的好
procedure TForm1.Button1Click(Sender: TObject);
const
cHexChars: array[0..15] of Char = '0123456789ABCDEF';
var
S: string;
I, J: Integer;
A: array of Byte;
P: PChar;
vTickCount: Longword; // 计时
begin
s := ''; //存放结果
SetLength(A, 1024000); // 测试数据开大一些
SetLength(S, Length(A) div 4);
for I := Low(A) to High(A) do //生成测试数据
A[I] := Random(2);
P := PChar(S);
vTickCount := GetTickCount;
{$R-,O-}
for I := 0 to Length(S) - 1 do //少循环N次
begin
J := I shl 2;
P^ := cHexChars[
A[J + 0] shl 3 or
A[J + 1] shl 2 or
A[J + 2] shl 1 or
A[J + 3] shl 0
];
Inc(P);
end;
{$R+,O+}
Caption := IntToStr(GetTickCount - vTickCount); // 输出处理时间
Memo1.Text := Copy(S, 1, 1024); // 输出部分结果
end;
SetLength(A, 1024000); // 测试数据开大一些
SetLength(S, Length(A) div 4);
for I := Low(A) to High(A) do //生成测试数据
A[I] := Random(2);
P := PChar(S);
vTickCount := GetTickCount;
{$R-}
for I := 0 to Length(S) - 1 do
begin
J := I shl 2;
P^ := cHexChars[
A[J + 0] shl 3 or
A[J + 1] shl 2 or
A[J + 2] shl 1 or
A[J + 3] shl 0
];
Inc(P);
end;
Caption := IntToStr(GetTickCount - vTickCount); // 输出处理时间
Memo1.Text := Copy(S, 1, 1024); // 输出部分结果
end;Top
30 楼hailants(hailants)回复于 2006-12-27 13:49:13 得分 0
下面这个函数测过速度了,转换100位的二进制消耗时间在400~500 ns 之间,不过肯定还不是最快的
S是源数组,T是目的数组,Len输入源数组转换长度,返回转换后目的数组长度.
注意:目的数组必须事先定义长度,并且是等于或大于转换后的长度,否则必出错
procedure BinToHexEx(const S;var T;var Len:Word);
var
i,j,Size:Word;
a,b:pchar;
begin
a:=pchar(s);
b:=pchar(T);
i:=Len;
Size:=i div 8;
if (i mod 8)<>0 then Size:=Size+1;
Len:=Size;
Fillchar(b[0],Size,$0);
while i>0 do
begin
for j:=0 to 7 do
begin
b[Size-1]:=char(byte(b[Size-1]) or (byte(a[i-1]) shl j));
i:=i-1;
if i=0 then
break;
end;
Size:=Size-1;
end;
end;Top
31 楼wywry(Wyatt)回复于 2006-12-27 13:49:19 得分 0
呵,牛,我在我的电梯上测试一下:数据产生量为(10240000)千万
zswang(伴水清清)(专家门诊清洁工):16ms
maozefa(阿发伯) :47ms
:))Top
32 楼hailants(hailants)回复于 2006-12-27 13:50:09 得分 0
如果要最快的话,就要考虑ASM代码了....Top
33 楼maozefa(阿发伯)回复于 2006-12-27 13:55:35 得分 0
to zswang(伴水清清)
我也在我的机器上测试过你的代码,确实是16毫秒,我修改后测试为15-16毫秒之间,但是多次测试,15的次数多些,我姑且认定差不多少了1毫秒!
至于wywry(Wyatt) 的测试结果,始料未及,哈哈。Top
34 楼maozefa(阿发伯)回复于 2006-12-27 14:00:00 得分 0
按wywry(Wyatt)的测试,zswang的百万级和千万级是一样的时间,那你的机器快的不得了啊!!!Top
35 楼wywry(Wyatt)回复于 2006-12-27 14:09:15 得分 0
配置也不高,是P42.6,内存768M
Top
36 楼hailants(hailants)回复于 2006-12-27 14:13:18 得分 0
汗,都用查表,是不是忽略了点什么,主的意思似乎是指转换后的数据仍然存放数组中,上面两位的怎么直接就变成字符串了....
Top
37 楼maozefa(阿发伯)回复于 2006-12-27 14:15:32 得分 0
to wywry(Wyatt)
我用千万级数据重新测试2段代码,结果是:
zswang(伴水清清)(专家门诊清洁工):47ms - 62ms
maozefa(阿发伯) :46ms - 47ms
Top
38 楼wq_quake(Paladin 象风一样的游侠)回复于 2006-12-27 14:21:08 得分 0
速度和空间是互相矛盾的,由逆向思维也可以知道要想换得速度就得利用空间。建立一个字符串s='0123456789abcdef';对于二进制就通过位操作求出其值对应下标的s[四位二进制值]就可以得到十六进制码。Top
39 楼wywry(Wyatt)回复于 2006-12-27 14:26:01 得分 0
我的测试也没错误,我是使用delphi2006
我测试N遍,基本都是那样的结果Top
40 楼hailants(hailants)回复于 2006-12-27 14:26:12 得分 0
倒,又一个没听明白的,上面的方法我也知道,我的意思是说他们转换出来的十六进制码是字符数据......
楼主似乎想要是数值...Top
41 楼maozefa(阿发伯)回复于 2006-12-27 14:30:29 得分 0
to wywry(Wyatt)
你检查2段代码都是千万级?算了,争这个没意思了,主要是探讨一下。祝大家开心!Top
42 楼hailants(hailants)回复于 2006-12-27 14:35:21 得分 0
......我测zswang(伴水清清)(专家门诊清洁工):的也是15-16.....Top
43 楼wywry(Wyatt)回复于 2006-12-27 14:48:10 得分 0
呵,是的,一样没错误
见下面:
你的代码:
procedure TForm2.Button4Click(Sender: TObject);
const
// Arr1: array[0..3] of Integer = (8, 4, 2, 1);
cHexChars: array[0..15] of Char = '0123456789ABCDEF';
var
s: string;
I, J, K, w_code: Integer;
ar: array of Byte;
vTickCount: Longword; // 计时
begin
s := ''; //存放结果
w_code := 0; //存放临时转换的字符
SetLength(Ar, 10240000); // 测试数据开大一些
// 4个二进制换一个十六进制 //S[I div 4 + 1] := cHexChars[w_code];一起打开
SetLength(S, Length(Ar) div 4);
//Randomize;//先确定结果是一致的
for I := Low(Ar) to High(Ar) do //生成测试数据
Ar[I] := Random(2);
J := 1;
vTickCount := GetTickCount;
for I := Low(Ar) to High(Ar) do
begin
// w_code := w_code + Ar[I] * Arr1[I and 3];
// if (I + 1) and 3 = 0 then
// begin
// S := S + IntToHex(w_code, 1); // 172毫秒
// S := S + cHexChars[w_code]; // 125毫秒
// S[I div 4 + 1] := cHexChars[w_code]; // 16毫秒 // 关键
// end;
K := (I - Low(Ar)) and 3;
w_code := w_code or Ar[I] shl (3 - K);
if K = 3 then
begin
S[J] := cHexChars[w_code];
w_code := 0;
Inc(J);
end;
end;
Caption := IntToStr(GetTickCount - vTickCount); // 输出处理时间
// RichEdit1.Text := S;//Copy(S, 1, 1024); // 输出部分结果
end;
zswang(伴水清清):
procedure TForm2.Button2Click(Sender: TObject);
const
cHexChars: array[0..15] of Char = '0123456789ABCDEF';
var
S: string;
I, J: Integer;
A: array of Byte;
P: PChar;
vTickCount: Longword; // 计时
begin
s := ''; //存放结果
SetLength(A, 10240000); // 测试数据开大一些
SetLength(S, Length(A) div 4);
for I := Low(A) to High(A) do //生成测试数据
A[I] := Random(2);
P := PChar(S);
vTickCount := GetTickCount;
{$R-,O-}
for I := 0 to Length(S) - 1 do //少循环N次
begin
J := I shl 2;
P^ := cHexChars[
A[J + 0] shl 3 or
A[J + 1] shl 2 or
A[J + 2] shl 1 or
A[J + 3] shl 0
];
Inc(P);
end;
{$R+,O+}
Caption := IntToStr(GetTickCount - vTickCount); // 输出处理时间
// RichEdit1.Text := S1;//Copy(S, 1, 1024); // 输出部分结果
end;
Top
44 楼maozefa(阿发伯)回复于 2006-12-27 14:59:01 得分 0
晕,我修改和比较的是他第一次回复的代码!!!他第二次的回复代码是在我回复之后重新写的,我根本就没看。
Top
45 楼wywry(Wyatt)回复于 2006-12-27 15:02:51 得分 0
晕,感谢!Top
46 楼maozefa(阿发伯)回复于 2006-12-27 15:07:56 得分 0
我只是想看看用位操作比乘除快多少而已,如果楼主对速度追求很高的话,可使用ASM,当然也要是优化的ASM。Top
47 楼Radar2006(中华英雄)回复于 2006-12-27 16:34:15 得分 0
这种精神值得学习~!Top
48 楼wr960204(武稀松)回复于 2006-12-27 21:49:38 得分 0
查表法最快
const
ZZ:array[0..1,0..1,0..1,0..1] of char =
(
((
('0','1'),
('2','3')
),
(
('4','5'),
('6','7')
)),
((
('8','9'),
('A','B')
),
(
('C','D'),
('E','F')
))
);
function T(Array_Address: PByte; Num: Integer): string;
type
PBytes = ^TBytes;
TBytes = array[0..0] of Byte;
var
P : PBytes;
I : Integer;
begin
SetLength(Result, Num div 4);
P := PBytes(Array_Address);
for I := 0 to (Num div 4) - 1 do
begin
Result[I + 1] := ZZ[P^[4*I],P^[4*I+1],P^[4*I+2],P^[4*i+3]];
end;
end;
//调用例子
var
B:array[0..43] of Byte = (1,1,1,1,
1,1,0,1,
0,0,0,0,
0,1,0,1,
1,0,1,0,
0,1,0,1,
0,1,1,0,
1,0,1,0,
0,0,0,0,
1,0,0,0,
0,1,1,1);
procedure TForm1.Button1Click(Sender: TObject);
var
I:Integer;
begin
ShowMessage(T(@B,44));
end;
Top
49 楼lijun51888(sun)回复于 2006-12-27 22:50:05 得分 0
高手很多啊Top
50 楼xuancaoer(当回复为'mark'的时候请别给我分)回复于 2006-12-28 01:51:27 得分 0
markTop




