Python
numpy
shuffle

全要素が移動する(同じ位置に残らない)shuffle

More than 1 year has passed since last update.

8/25追記

最終版??
http://qiita.com/ttatsf/items/d4543ccc80e0fbe3891d#comment-3eccf9c112d73c505bc2

8/23追記

下記のプログラムだと全てのshuffleパターンが網羅されていない,という指摘がありました.
[0,1,2,3] → [1,0,3,2] のような奇遇交換の例.

利用にあたってはご注意ください.

8/21追記

最終的に2行になった模様(白目)

def shuffle_all_move(items):
    rand_ord = random.sample(range(len(items)), k=len(items))
    return [items[r] for i, r in sorted(zip(rand_ord, rand_ord[1:]+rand_ord[:1]))]

実行速度も僅かに上のコードの方がわずかに速いようです(コメント欄に計測結果を載せています)


8/19追記

コメント欄にある,ttatsfさんが作ってくださったものが一番気に入っています.
(ベースにある「サットロのアルゴリズム」は7of9さんに紹介頂きました)
他にもコメントでコードを書いてくださった皆様,ありがとうございました.

import random

def shuffle_all_move( items ):
  length = len( items )
  res = [ 0 ] * length
  rand_ord = random.sample( range( length ), k=length )
  for i in range(  length  ) :
    res[ rand_ord[ i ] ] = items[ rand_ord[ ( i + 1 ) % length ] ]
  return res

実にエレガント!
…というわけで,下の適当なDIY shuffleより上記を利用することをおススメしたいw


元の投稿

絶対に要素が移動するようなshuffleのpython+numpy実装です(配列の要素が3以上であることを仮定しているので注意).
手順
1. ランダムに要素を3グループに分割
2. グループ毎にshuffle
3. グループを1つスライドさせる.

1のランダムなグループ分割は,実際にはshuffleしてから配列を3つに分割することで実現.shuffleされた配列のグループをスライドさせても,元の配列の位置とは無関係になってしまうので,後でこのshuffleを元に戻す.これにより,元の配列と必ず違う位置に移動することが保障される.

このコードでは,下記の2つのページにあるコードを借用しています.
※ resettable_shuffle/reset_shuffle
http://qiita.com/EafT/items/9527cb30409b70106fd4
※ divide_list
http://fits.hatenablog.com/entry/2015/11/09/211012

def shuffle_all_move(_array):
    array = _array.copy()
    seed = random.randint(0,np.iinfo(np.int32).max)
    resettable_shuffle(array,seed)
    array = divide_list(array,3)
    if type(array[0]) == list:
        array = array[-1] + array[0] + array[1]
    else:
        array = np.vstack((array[-1], array[0],array[1]))
    return reset_shuffle(array,seed)

実行例

test_array = ['a','b','c','d','e','f']
print(test_array)
test_array = shuffle_all_move(test_array)
print(test_array)

['a', 'b', 'c', 'd', 'e', 'f']
['c', 'e', 'f', 'b', 'd', 'a']