web-dev-qa-db-ja.com

QuickCheckで単射関数を生成しますか?

QuickCheckを使用して任意の関数を生成しています。任意の injective 関数を生成したいと思います(つまり、f a == f bの場合のみa == b)。

thought私はそれを理解してもらいました:

newtype Injective = Injective (Fun Word Char) deriving Show

instance Arbitrary Injective where
  arbitrary = fmap Injective fun
    where
      fun :: Gen (Fun Word Char)
      fun = do
        a <- arbitrary
        b <- arbitrary
        arbitrary `suchThat` \(Fn f) ->
          (f a /= f b) || (a == b)

しかし、生成された関数が異なる入力を同じ出力にマップする場合があります。

私が欲しいもの:

  • fforfor allinputaおよびbfaと等しくないfbまたはaと等しいb

私が持っていると思うもの:

  • f存在する入力aおよびbwhere wherefaと等しくないfbまたはaと等しいb

どうすれば修正できますか?

6
ivan

問題を正しく特定しました。生成しているのは∃ a≠b. f a≠f bプロパティ(これはほとんどのランダム関数に当てはまります)の関数ですが、必要なのは∀ a≠b. f a≠f bです。個々の値を生成するには、他のすべての関数値について知っておく必要があるため、これを確認するのははるかに難しいプロパティです。

私はこれが一般的な入力タイプを保証することは可能ではないと思いますが、特にWordに対してできることは、すべての出力値を順番に事前計算することによって関数を「偽造」することです。完了し、その所定のチャートから読み取ります。これを実際に機能させるには、少し怠惰なfuが必要です。

import qualified Data.Set as Set

newtype Injective = Injective ([Char] {- simply a list without duplicates -})
 deriving Show

instance Arbitrary Injective where
  arbitrary = Injective . lazyNub <$> arbitrary

lazyNub :: Ord a => [a] -> [a]
lazyNub = go Set.empty
 where go _ [] = []
       go forbidden (x:xs)
        | x `Set.member` forbidden  = go forbidden xs
        | otherwise                 = x : go (Set.insert x forbidden) xs

これはあまり効率的ではなく、アプリケーションに問題があるかもしれませんが、おそらくあなたができる最善の方法です。

実際には、実際にInjectiveを関数として使用するには、値を[〜#〜] o [〜#〜]( log n)ルックアップ時間。残念ながら、Data.Map.Lazyは十分に怠惰ではないため、指数関数的に増加するマップのリストのようなものを手動でベイクする必要がある場合があります。

また、十分に大きな結果の型では、十分な値がないために単射関数を生成できない可能性があるという懸念もあります。実際、ジョセフが述べたように、これはここに当てはまります。この場合、lazyNub関数は無限ループに入ります。私はQuickCheckのためにこれはおそらく大丈夫だと思います。

2
leftaroundabout