整数的num2str函数能否更快?

3

我有一个名为 skj 的数组。 skj 包含 200 万行数字 (2000000x1 uint32)。

我想要计算以下内容:

string_skj = num2str(skj);

当我运行以上代码时,需要大约1分钟的时间,有没有更快的方法?


1
int2str比num2str更快。 - Adriaan
@IKavanagh:这会产生不同的输出。 - Daniel
@Daniel 是的,那完全是错的。谢谢。 - IKavanagh
1
@Adriaan 不是在我的电脑上:P 它们的执行是相等的。 - IKavanagh
2
另一个非常快速的“解决方案”,输出不完全相同,但类似 a = sprintf('%d\n',skj) - Hennadii Madan
显示剩余2条评论
5个回答

5
Hennadii Madan的答案让我想到是否有一种比标准的Matlab num2str(或int2str)更有效地处理列向量的方法,我已经想出了两种解决方案。

编辑: 在所有这些工作之后,@Luis Mendo进来把它全部推翻了 :'(

编辑: 现在@Daniel又改进了所有先前的选项!


给定我们的行向量V,如下:

V = uint32(randi(100, 200000, 1));

我们可以实现与之相同的结果。
A = num2str(V);

使用 *

B = char(strsplit(num2str(V.')).');

或者不使用 num2str 的错误检查

C = char(strsplit(sprintf('%d\n', V)).');
C = C(1:end-1, :); % Remove extraneous '\n'

BCA略有不同。 num2str在前面填充一个空格' ',而BC在后面填充一个空格。

在下面的例子中,DE都使用了前导的0进行填充,因此无法与ABC完全匹配。


基准测试

-----num2str() on row vector [Original]-----
Elapsed time is 3.501976 seconds.
  Name           Size              Bytes  Class    Attributes

  A         200000x3             1200000  char               

-----num2str() on column vector [IKavanagh modified from Hennadii Madan]-----
Elapsed time is 0.660878 seconds.
  Name           Size              Bytes  Class    Attributes

  B         200000x3             1200000  char               

-----sprintf() on row vector [IKavanagh]-----
Elapsed time is 0.582472 seconds.
  Name           Size              Bytes  Class    Attributes

  C         200000x3             1200000  char               

-----dec2base() on row vector [Luis Mendo]-----
Elapsed time is 0.042563 seconds.
  Name           Size              Bytes  Class    Attributes

  D         200000x3             1200000  char



-----myfastint2str() on row vector [Daniel]-----
Elapsed time is 0.011894 seconds.
  Name           Size              Bytes  Class    Attributes

  E         200000x3             1200000  char 

代码

clear all
close all
clc

V = uint32(randi(100, 200000, 1));

for k = 1:50000
    tic(); elapsed = toc(); % Warm up tic/toc
end

disp('-----num2str() on row vector [Original]-----');
tic;
A = num2str(V);
toc, whos A

disp('-----num2str() on column vector [IKavanagh modified from Hennadii Madan]-----');
tic;
B = char(strsplit(num2str(V.')).');
toc, whos B

disp('-----sprintf() on row vector [IKavanagh]-----');
tic;
C = char(strsplit(sprintf('%d\n', V)).');
C = C(1:end-1, :); % Remove extraneous '\n'
toc, whos C

disp('-----dec2base() on row vector [Luis Mendo]-----');
tic;
D = dec2base(V, 10);
toc, whos D

disp('-----myfastint2str() on row vector [Daniel]-----');
tic;
E = myfastint2str(V);
toc, whos E


到目前为止,这是最接近的了,我想知道我们是否能解决空格不存在的问题。 - Hennadii Madan
@HennadiiMadan 这个空格是为了确保每一行的维度相同。我们可以将其移动到末尾,但说实话,我更喜欢它在前面。 - IKavanagh
我更新了我的代码,现在只生成一个nx3的数组。应该会更快。 - Daniel
@IKavanagh 我刚刚检查了一下,在我的机器上,sprintf版本无论是否转置都能同样快速。[笑] - Hennadii Madan
@Daniel,现在它比以前的解决方案都要快得多! - IKavanagh
@HennadiiMadan 是的,我知道,那是因为sprintf的向量化版本的工作方式。基本上,对于这个例子,它将行向量和列向量视为相同。 - IKavanagh

4
以下代码在我的电脑上运行速度更快:
y = dec2base(skj,10);

以下是一个快速测试:

>> skj = uint32(2^32*rand(1e6,1)); %// random data

>> tic, y = num2str(skj); toc
Elapsed time is 22.823348 seconds.

>> tic, z = dec2base(skj,10); toc
Elapsed time is 1.235942 seconds.

请注意,使用dec2base会产生前导零而不是前导空格。
>> y(1:5,:)
ans =
3864067979
1572155259
1067755677
2492696731
 561648530

>> z(1:5,:)
ans =
3864067979
1572155259
1067755677
2492696731
0561648530

谢谢你用你的解决方案让我大吃一惊! - IKavanagh
@IKavanagh 这是在说 ' 是转置运算符!:-P http://chat.stackoverflow.com/transcript/81987?m=26687283#26687283 http://chat.stackoverflow.com/transcript/81987?m=26685812#26685812 - Luis Mendo
2
@Dev-iL警告了我。我没有想到你的力量如此强大。 - IKavanagh
2
@IKavanagh,这是一句非常棒的赞美,来自于如此伟大的绝地大师! - Luis Mendo

4

如果您自己实现 int2str 函数,那么您可以远远超越原始函数的性能。

function [ o ] = myfastint2str( x )
maxvalue=max(x(:));
%maxvalue=intmax(class(x));%Alternative implementation based on class
required_digits=ceil(log(double(maxvalue+1))/log(10));
o=repmat(x(1)*0,size(x,1),required_digits);%initialize array of required size
for c=size(o,2):-1:1
   o(:,c)=mod(x,10);
   x=(x-o(:,c))/10;
end
o=char(o+'0');
end

对于示例输入,我的函数所需时间少于0.15秒,而int2str和num2str都需要约15秒。 输出略有不同,因为它会生成前导零而不是空格。

@IKavanagh:在看到你的基准测试只使用三位数字后,我删除了预填充。代码始终生成10位数字是第三行(注释)。 - Daniel
这是一个更好的改进。现在它将根据向量中最大数字的大小来改变宽度。 - IKavanagh

1
如果你真的需要提高速度,你是否考虑过用C语言编写MEX函数扩展?这可能有点复杂,但如果你有一些可以轻松用C/C++编码的小例程,那么投资时间是值得的。编译后,MEX函数可以像.m函数一样从MATLAB命令提示符中调用。
更多细节请参见http://www.mathworks.com/help/matlab/call-mex-files-1.html

1
我认为在这种情况下编写mex函数并不是正确的选择。对于简单算法和大数据,调用mex函数的开销通常太大了。Mex非常适合复杂的代码(特别是许多循环)。 - Daniel
1
如果你已经尝试过了,我理解。然而,在我的经验中,即使是小函数和大数据,开销也不是问题。MEx函数的加载方式与MATLAB内置函数相同。MEx接口只会在数组周围创建一个C结构包装器,并且MEx函数将访问指向数值数组的指针,因此不涉及复制。 - gariepy
根据我的经验,使用高效的M代码实现是最佳选择,并且我提供了最快的答案,我认为我是正确的。然而,我不明白为什么这个答案收到了负评,它是一个合理的想法。 - Daniel
这很有趣...我已经好几年没有对MEx文件进行基准测试了... MATLAB可能已经显着提高了.m文件的性能,使它们与MEx实现相当。 我得回去再次测试一些旧例程。 - gariepy
@daniel 你只需要做一个mex调用!mex绝对可以解决这个问题。例如:在我的计算机上,对一个一百万个条目的字符串单元数组调用Matlab的str2double需要25秒钟。调用a mex version只需0.24秒即可转换为双精度!快100倍。在某些情况下,Matlab并不高效,编写一个C++函数虽然很麻烦,但速度可以提升很多。 - Matthew Gunn

0

警告:输出结果可能是可行的,但是不正确。

编辑:一个超级快速的“解决方案”输出不是一列,而是一个带有换行符作为分隔符的字符串。如果您尝试打印它,它看起来会相同。

>> tic;a = sprintf('%d\n',skj);toc
Elapsed time is 0.422143 seconds

编辑: 旧的“解决方案”

尝试在之前和之后进行转置。例如num2str(skj.').'

>> skj = ones(2000000,1,'uint32');
>> tic;num2str(skj);toc
Elapsed time is 23.305860 seconds.
>> tic;num2str(skj.');toc
Elapsed time is 1.044551 seconds.

@HennadiiMadan 为什么 num2str(skj.')num2str(skj) 更快呢? - David
@rayryeng,使用函数还可以最大化可用的优化,这在交互模式下是不可用的。 - Andras Deak -- Слава Україні
我也不知道。但是num2str(skj.').'会产生错误的输出,因为转置对字符数组的影响。你应该将它改为num2str(skj.') - IKavanagh
1
它可能更快,但它只是不能产生正确的结果。请尝试使用 skj = uint32(1:200000)'。它要么创建一个包含所有元素的单个字符串,要么在转置时将每个字符放在单独的一行上,我相信这不是任何人想要的。 - zelanix
2
大家好,这个转置问题应该突出一个巨大的时间差异源。当将列向量转换为字符串时,Matlab必须确定构建正方形字符矩阵的最小公共字符长度。但是,将单行转换时则不需要这样做!这意味着要确定200k个数字的可打印位数... - Andras Deak -- Слава Україні
显示剩余8条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接