2017-10-28 81 views
2

我想到了两种不同的方法,但都显得很丑陋。朱莉娅:如何获得一个给定的字符串s的随机排列?

  1. 变换字符串s到一个数组a通过split婷,然后再次使用sample(a, length(s), replace=false)join阵列成一个字符串

  2. 优惠RandomPermutationr长度length(s)joins[i]ir

什么是正确的方法?不幸的是,没有匹配sample(::String, ::Int64; replace=false)的方法。

+2

'join(collect(s)[randperm(length(s))])' –

+1

至少第二种方式有unicode问题。 '函数样例(s :: String)join(getindex([i for s in],randperm(length(s))))end; sample(“ďaľšý”)似乎也适用于unicode。 (但丹的解决方案更好!:) – Liso

+1

如果您知道这是一个ASCII字符串:'[randperm(end)]''。否则:'加入(shuffle!(collect(s)))'。也许是一个很好的写法:'s | collect |> shuffle! |>加入'。 – DNF

回答

4

也许定义String一个shuffle方法构成类型盗版,但是,无论如何,这里有一个建议implemetation:

Base.shuffle(s::String) = isascii(s) ? s[randperm(end)] : join(shuffle!(collect(s))) 
2

如果你想挤掉从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 

对于大字符串是超过4倍的ASCII和3倍更快UTF-8快。

遗憾的是凌乱的 - 所以我宁愿把它作为一个练习。但是,它只使用导出的函数,所以它不是黑客。

+1

比什么更快?如果练习,那么你可能不需要运行整个长度:'all(((codeunit(s,i)&0xc0)== 0x80)for i in 1:sizeof(s))'而不是'sizeof(s) ==长度(s)'。虽然我不确定发电机在哪里 - 但他们在Julia似乎很慢。 – Liso

+0

我使用'sizeof(s)== length(s)',因为它们稍后在UTF-8部分中使用。如果我使用'ascii(s)',我将不得不再次计算它们,所以这样我就避免运行'ascii(s)'。但是我同意它稍微降低了ASCII部分的性能(不过对于ASCII字符串来说,不管怎么说都不得不走到尽头)。 –

+0

和速度比较是在第一个答案中提出的实现'Base.shuffle(s :: String)'。 –

1

在博古米尔斯基的回答的优化技巧的启发,下面是几乎相同的性能版本,但更清楚一点(在我看来),并利用其本身可以是一个有价值的第二效用函数:

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 

例时机:

julia> using BenchmarkTools 

julia> s = "ďaľšý" 

julia> @btime shuffle($s)  # shuffle from DNF's answer 
    831.200 ns (9 allocations: 416 bytes) 
"ýľďša" 

julia> @btime shufflefast($s) # shuffle from this answer 
    252.224 ns (5 allocations: 432 bytes) 
"ľýďaš" 

julia> @btime kaminskishufflefast($s) # shuffle from Kaminski's answer 
    197.345 ns (4 allocations: 384 bytes) 
"ýašďľ" 
0

编辑:好一点的性能 - 见代码注释

这是博古米尔卡明斯基的ANS WER其中我试图避免计算长度(*),如果它不是必要的:

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 

实施例定时ASCII字符串:

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) 

差过小,有时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是一个小但明显更快这里。比丹的解决方案多一点分配比博略米尔的功能和少一些的分配。

(*) - 我一点希望,在朱莉娅的字符串实施将在未来和长度速度可能会更快,比现在。

+0

从这个问题(和答案)的外卖可能是更好的'isascii '定义为:'fastisascii(s)=(for l in 1:sizeof(s)codeunit(s,l)&0xc0 == 0x80 && return false; end; true)'。你认为它应该成为公关?也许朱莉娅codegen应该改进,所以目前'isascii'生成类似的代码。 –

+0

@DanGetz它可能很好! :)'isascii(s :: String)=(for l in 1:sizeof(s)codeunit(s,l)> = 0x80 && return false; end; true)'可以工作得更快(也更快)。它可能必须专门化,因为可能有不同的字符串实现。字符串的具体类型是使用UTF-8编码,所以这个方法必须很好(我们需要更多的测试?)。关于拉请求 - 你的贡献比我的更大,我对github很新颖。所以我会很高兴你做这个公关! :) – Liso

+1

fastshuffle2中的“isascii”是受@edit Base.is_valid_continuation(codeunit(“ - ”,1))启发而来的,但可以根据@edit isascii简化(“”)看着 :) ... – Liso