String 朱莉娅:如何得到给定字符串的随机排列?
我想了两种不同的方法,但都很难看String 朱莉娅:如何得到给定字符串的随机排列?,string,julia,permutation,String,Julia,Permutation,我想了两种不同的方法,但都很难看 通过拆分将字符串s转换为数组a,然后使用sample(a,length(s),replace=false)和将数组再次合并为字符串 在r中为i获取长度s的随机排列r,并将s[i]的单个s[i]连接起来 正确的方法是什么?不幸的是,没有方法匹配示例(::String,::Int64;replace=false) 也许为String定义shuffle方法会构成类型盗版,但无论如何,这里有一个建议的实现: Base.shuffle(s::String) = isasc
s
转换为数组a
,然后使用sample(a,length(s),replace=false)
和将数组再次合并为字符串
r
中为i
获取长度s
的随机排列r
,并将s[i]
的单个s[i]
连接起来
正确的方法是什么?不幸的是,没有方法匹配
示例(::String,::Int64;replace=false)
也许为String
定义shuffle
方法会构成类型盗版,但无论如何,这里有一个建议的实现:
Base.shuffle(s::String) = isascii(s) ? s[randperm(end)] : join(shuffle!(collect(s)))
如果您想从
shuffle
中挤出性能,那么您可以考虑:
function shufflefast(s::String)
ss = sizeof(s)
l = length(s)
ss == l && return String(shuffle!(copy(Vector{UInt8}(s))))
v = Vector{Int}(l)
i = start(s)
for j in 1:l
v[j] = i
i = nextind(s, i)
end
p = pointer(s)
u = Vector{UInt8}(ss)
k = 1
for i in randperm(l)
for j in v[i]:(i == l ? ss : v[i+1]-1)
u[k] = unsafe_load(p, j)
k += 1
end
end
String(u)
end
对于大字符串,ASCII比UTF-8快4倍多,UTF-8快3倍多
不幸的是,它很凌乱,所以我宁愿把它当作一种锻炼。但是,它只使用导出的函数,因此不是黑客。受Bogumil Kaminski答案中优化技巧的启发,以下版本的性能几乎相同,但更清晰一些(在我看来),并且使用了第二个实用函数,其本身可能有价值:
function strranges(s) # returns the ranges of bytes spanned by chars
u = Vector{UnitRange{Int64}}()
sizehint!(u,sizeof(s))
i = 1
while i<=sizeof(s)
ii = nextind(s,i)
push!(u,i:ii-1)
i = ii
end
return u
end
function shufflefast(s)
ss = convert(Vector{UInt8},s)
uu = Vector{UInt8}(length(ss))
i = 1
@inbounds for r in shuffle!(strranges(s))
for j in r
uu[i] = ss[j]
i += 1
end
end
return String(uu)
end
编辑:性能稍微好一点-参见代码注释 这是Bogumil Kaminski的回答,我试图避免计算长度(*),如果没有必要:
function shufflefast2(s::String)
ss = sizeof(s)
local l
for l in 1:ss
#if ((codeunit(s,l) & 0xc0) == 0x80)
if codeunit(s,l)>= 0x80 # edit (see comments bellow why)
break
end
end
ss == l && return String(shuffle!(copy(Vector{UInt8}(s))))
v = Vector{Int}(ss)
i = 1
l = 0
while i<ss
l += 1
v[l] = i
i = nextind(s, i)
end
v[l+1] = ss+1 # edit - we could do this because ss>l
p = pointer(s)
u = Vector{UInt8}(ss)
k = 1
for i in randperm(l)
# for j in v[i]:(i == l ? ss : v[i+1]-1)
for j in v[i]:v[i+1]-1 # edit we could do this because v[l+1] is defined (see above)
u[k] = unsafe_load(p, j)
k += 1
end
end
String(u)
end
差异太小,有时bkshufflefast更快。表现必须平等。整个长度必须计算,并且有相同的分配
unicode字符串的计时示例:
julia> srand(1234);@btime for i in 1:100 danshufflefast(s) end
24.964 μs (500 allocations: 42.19 KiB)
julia> srand(1234);@btime for i in 1:100 bkshufflefast(s) end
20.882 μs (400 allocations: 37.50 KiB)
julia> srand(1234);@btime for i in 1:100 shufflefast2(s) end
19.038 μs (400 allocations: 40.63 KiB)
shufflefast2在这里稍微快一点,但显然更快。比Bogumil函数的分配多一点,比Dan的解的分配少一点
(*)-我有点希望Julia中的字符串实现将来会更快,长度也会比现在快得多
join(collect(s)[randperm(length(s))])
至少第二种方式与unicode有问题<代码>函数示例(s::String)连接(getindex([i代表s中的i],randperm(长度)))结束;示例(“ďaľšý”)似乎也适用于unicode。(但丹的解决方案更好!:)如果你知道这是一个ASCII字符串:s[randperm(end)]
。否则:加入(洗牌!(收集))
。也许这是一个很好的方法:s>collect>shuffle!|>加入
。比什么更快?如果练习,那么您可能不需要运行整个长度:all((1:sizeof(s)中i的codeunit(s,i)和0xc0)==0x80))
而不是sizeof(s)==length(s)
。虽然我不确定这里的生成器-它们在Julia中似乎很慢。我使用sizeof(s)=length(s)
,因为它们后来在UTF-8部件中使用。如果我使用ascii(s)
,我将不得不在以后再次计算它们,这样我就避免了运行ascii(s)
。但我同意它会降低ASCII部分的性能一点(与ASCII字符串的性能没有多大差别),而且速度比较是与第一个答案中提出的实现Base.shuffle(s::string)
进行的。我们可能可以分配v=Vector{Int}(ss)
并检查nextind是否返回比ss更大的索引以获得一些速度…从这个问题(和答案)中删除可能是更好的isascii
定义为:fastisascii(s)=(对于1中的l:sizeof(s)codefunit(s,l)&0xc0==0x80&&return false;end;true)
。你认为它应该成为公关吗?也许应该改进Julia codegen,使当前的isascii
生成类似的代码。@DanGetz它可能很好!:)isascii(s::String)=(对于1中的l:sizeof(s)codefunit(s,l)>=0x80&&return false;end;true)
也可以工作(更快)。它可能必须专门化,因为可能有不同的字符串实现。字符串具体类型正在使用UTF-8编码,所以这个方法必须很好(我们可能还需要一些测试?)。关于pull请求-您的贡献比我的更大,我对github还很陌生。所以我很高兴你做了这个公关!:)我在fastshuffle2中的“isascii”是受@edit Base的启发。它是有效的延续(codeunit(“-”,1))
,但它可以根据@edit isascii(“”
)简化(你启发我看:)。。。
julia> srand(1234);@btime for i in 1:100 danshufflefast("test") end
19.783 μs (500 allocations: 34.38 KiB)
julia> srand(1234);@btime for i in 1:100 bkshufflefast("test") end
10.408 μs (300 allocations: 18.75 KiB)
julia> srand(1234);@btime for i in 1:100 shufflefast2("test") end
10.280 μs (300 allocations: 18.75 KiB)
julia> srand(1234);@btime for i in 1:100 danshufflefast(s) end
24.964 μs (500 allocations: 42.19 KiB)
julia> srand(1234);@btime for i in 1:100 bkshufflefast(s) end
20.882 μs (400 allocations: 37.50 KiB)
julia> srand(1234);@btime for i in 1:100 shufflefast2(s) end
19.038 μs (400 allocations: 40.63 KiB)