如何在Agda中对列表进行洗牌?

huangapple go评论47阅读模式
英文:

How to shuffle a list i agda?

问题

I would like to randomly shuffle a list in agda.

I thought about taking random n elements from the beginning and adding them to the end, but I couldn't get the random part to work from this approach.

Is this possible with pattern matching or do I really have to figure out random number generation?

Current state:

shuffle : {A : Set} -> List A -> List A
shuffle [] = []
shuffle (x ∷ []) = x ∷ []
shuffle (x ∷ x₁ ∷ xs) = ?

我想在Agda中对一个列表进行随机洗牌。

我考虑过从开头随机选择 n 个元素并将它们添加到末尾,但是我无法使这种方法中的随机部分工作。

这是否可以使用模式匹配来实现,还是我真的必须解决随机数生成的问题?

当前状态:

shuffle : {A : Set} -> List A -> List A
shuffle [] = []
shuffle (x ∷ []) = x ∷ []
shuffle (x ∷ x₁ ∷ xs) = ?
英文:

I would like to randomly shuffle a list in agda.

I thought about taking random n elements from the beginning and adding them to the end, but I couldn't get the random part to work from this approach.

Is this possible with pattern matching or do I really have to figure out random number generation?

Current state:

shuffle : {A : Set} -> List A -> List A
shuffle [] = []
shuffle (x ∷ []) = x ∷ []
shuffle (x ∷ x₁ ∷ xs) = ?

答案1

得分: 1

我认为实现Fisher-Yates洗牌算法的直接方式是使用一个monadic效果来生成随机数:

open import Data.Nat
open import Data.Fin
open import Data.Vec

variable
  A : Set _

open import Data.Product hiding (swap)

swap :  {n}  Fin (suc n)  Vec A (suc n)  (A × Vec A n)
swap         zero    (x₀  xs) = x₀ , xs
swap {A = A} (suc i) (x₀  xs) = go i xs
  where
    go :  {n}  Fin n  Vec A n  A × Vec A n
    go zero    (x  xs) = x , x₀  xs
    go (suc i) (x  xs) with go i xs
    ... | (x , xs) = x , x  xs

open import Category.Monad

module Shuffle {M} (Mon : RawMonad M) (randomFin :  {n}  M (Fin (suc n))) where
  open RawMonad Mon

  shuffle :  {n}  Vec A n  M (Vec A n)
  shuffle [] = pure []
  shuffle {n = suc n} xs@(_  _) = do
    k  randomFin
    let (x , xs) = swap k xs
    xs  shuffle xs
    pure (x  xs)

那么我们如何生成随机的Fin n?@mudri的评论指向了Data.Nat.PseudoRandom.LCG,它建议使用带有类型step,并使用n的简单余数来生成Fin n

module ShuffleState (step :   ) where
  open import Category.Monad.State
  open RawMonadState (StateMonadState )

  randomℕ : State  
  randomℕ = modify step = get

  open import Data.Nat.DivMod

  randomFin :  {n}  State  (Fin (suc n))
  randomFin = (_mod _) <$> randomℕ

  open Shuffle monad randomFin public

示例用法:

module _ where
  open import Data.Nat.PseudoRandom.LCG
  open ShuffleState (step glibc)

  test : Vec (Fin 10) 10
  test = proj₁ (shuffle (allFin 10) 0)

请注意,这并不证明输出确实是输入的排列。如果您需要这一点,我认为更容易改变shuffleswap以产生一个排列证明(例如,像这里那样),而不是直接操作输入向量并在外部进行证明。

编辑以添加
至少在Idris 2中,我们可以通过在适当的地方使用线性类型来证明shuffle在被洗牌的向量中是线性的:

import Data.Vect
import Data.Fin

swap : Fin (S n) -> (1 xs : Vect (S n) a) -> Vect (S n) a
swap FZ     (x0 :: xs) = (x0 :: xs)
swap (FS i) (x0 :: xs) = go i xs
  where
    go : Fin k -> Vect k a -> Vect (S k) a
    go FZ     (x :: xs) = x :: x0 :: xs
    go (FS i) (x :: xs) =
      let (x' :: xs') = go i xs in
      x' :: x :: xs';

shuffle: Monad m => (randomFin : {k : _} -> m (Fin (S k))) -> {n : _} -> (1 xs : Vect n a) -> m (Vect n a)
shuffle [] = pure []
shuffle (x::xs) = do
  k <- randomFin
  let (x' :: xs') = swap k (x :: xs)
  xs'' <- shuffle xs'
  pure (x' :: xs'')

请注意shuffle签名中xs的类型。我认为这里的参数化和线性类型应该足够说服您shuffle确实对其输入进行了洗牌。

英文:

I think the straightforward way to implement Fisher-Yates shuffle is to use a monadic effect for generating random numbers:

open import Data.Nat
open import Data.Fin
open import Data.Vec
variable
A : Set _
open import Data.Product hiding (swap)
swap : ∀ {n} → Fin (suc n) → Vec A (suc n) → (A &#215; Vec A n)
swap         zero    (x₀ ∷ xs) = x₀ , xs
swap {A = A} (suc i) (x₀ ∷ xs) = go i xs
where
go : ∀ {n} → Fin n → Vec A n → A &#215; Vec A n
go zero    (x ∷ xs) = x , x₀ ∷ xs
go (suc i) (x ∷ xs) with go i xs
... | (x′ , xs′) = x′ , x ∷ xs′
open import Category.Monad
module Shuffle {M} (Mon : RawMonad M) (randomFin : ∀ {n} → M (Fin (suc n))) where
open RawMonad Mon
shuffle : ∀ {n} → Vec A n → M (Vec A n)
shuffle [] = pure []
shuffle {n = suc n} xs@(_ ∷ _) = do
k ← randomFin
let (x′ , xs′) = swap k xs
xs″ ← shuffle xs′
pure (x′ ∷ xs″)

So how do we produce random Fin ns? @mudri's comment points us to Data.Nat.PseudoRandom.LCG which suggests using step with a state of type , and using simple remainder by n to produce a Fin n:

module ShuffleState (step : ℕ → ℕ) where
open import Category.Monad.State
open RawMonadState (StateMonadState ℕ)
randomℕ : State ℕ ℕ
randomℕ = modify step ⊛&gt; get
open import Data.Nat.DivMod
randomFin : ∀ {n} → State ℕ (Fin (suc n))
randomFin = (_mod _) &lt;$&gt; randomℕ
open Shuffle monad randomFin public

Example usage:

module _ where
open import Data.Nat.PseudoRandom.LCG
open ShuffleState (step glibc)
test : Vec (Fin 10) 10
test = proj₁ (shuffle (allFin 10) 0)

Note that this doesn't prove that the output is indeed a permutation of the input. If you need that, I think it's easier to change shuffle and swap to produce a permutation witness (e.g. like this) instead of operating on the input vector directly and proving it externally.

Edited to add:
In Idris 2 at least, we can prove that shuffle is linear in the shuffled vector by just using linear types where they matter:

import Data.Vect
import Data.Fin
swap : Fin (S n) -&gt; (1 xs : Vect (S n) a) -&gt; Vect (S n) a
swap FZ     (x0 :: xs) = (x0 :: xs)
swap (FS i) (x0 :: xs) = go i xs
where
go : Fin k -&gt; Vect k a -&gt; Vect (S k) a
go FZ     (x :: xs) = x :: x0 :: xs
go (FS i) (x :: xs) =
let (x&#39; :: xs&#39;) = go i xs in
x&#39; :: x :: xs&#39;
shuffle: Monad m =&gt; (randomFin : {k : _} -&gt; m (Fin (S k))) =&gt; {n : _} -&gt; (1 xs : Vect n a) -&gt; m (Vect n a)
shuffle [] = pure []
shuffle (x::xs) = do
k &lt;- randomFin
let (x&#39; :: xs&#39;) = swap k (x :: xs)
xs&#39;&#39; &lt;- shuffle xs&#39;
pure (x&#39; :: xs&#39;&#39;)

Note the type of xs in the signature of shuffle. I think parametricity + linearity together here should be sufficiently convincing that shuffle indeed shuffles its input.

huangapple
  • 本文由 发表于 2023年4月10日 20:49:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/75977277.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定