-
Notifications
You must be signed in to change notification settings - Fork 139
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #498 from gksato/optimize-nextperm
Optimize Mutable.nextPermutation and add {next/prev}permutation(By)
- Loading branch information
Showing
11 changed files
with
458 additions
and
51 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
{-# LANGUAGE BangPatterns #-} | ||
{-# LANGUAGE FlexibleContexts #-} | ||
module Bench.Vector.Algo.NextPermutation (generatePermTests) where | ||
|
||
import qualified Data.Vector.Unboxed as V | ||
import qualified Data.Vector.Unboxed.Mutable as M | ||
import qualified Data.Vector.Generic.Mutable as G | ||
import System.Random.Stateful | ||
( StatefulGen, UniformRange(uniformRM) ) | ||
|
||
-- | Generate a list of benchmarks for permutation algorithms. | ||
-- The list contains pairs of benchmark names and corresponding actions. | ||
-- The actions are to be executed by the benchmarking framework. | ||
-- | ||
-- The list contains the following benchmarks: | ||
-- - @(next|prev)Permutation@ on a small vector repeated until the end of the permutation cycle | ||
-- - Bijective versions of @(next|prev)Permutation@ on a vector of size @n@, repeated @n@ times | ||
-- - ascending permutation | ||
-- - descending permutation | ||
-- - random permutation | ||
-- - Baseline for bijective versions: just copying a vector of size @n@. Note that the tests for | ||
-- bijective versions begins with copying a vector. | ||
generatePermTests :: StatefulGen g IO => g -> Int -> IO [(String, IO ())] | ||
generatePermTests gen useSize = do | ||
let !k = useSizeToPermLen useSize | ||
let !vasc = V.generate useSize id | ||
!vdesc = V.generate useSize (useSize-1-) | ||
!vrnd <- randomPermutationWith gen useSize | ||
return | ||
[ ("nextPermutation (small vector, until end)", loopPermutations k) | ||
, ("nextPermutationBijective (ascending perm of size n, n times)", repeatNextPermutation vasc useSize) | ||
, ("nextPermutationBijective (descending perm of size n, n times)", repeatNextPermutation vdesc useSize) | ||
, ("nextPermutationBijective (random perm of size n, n times)", repeatNextPermutation vrnd useSize) | ||
, ("prevPermutation (small vector, until end)", loopRevPermutations k) | ||
, ("prevPermutationBijective (ascending perm of size n, n times)", repeatPrevPermutation vasc useSize) | ||
, ("prevPermutationBijective (descending perm of size n, n times)", repeatPrevPermutation vdesc useSize) | ||
, ("prevPermutationBijective (random perm of size n, n times)", repeatPrevPermutation vrnd useSize) | ||
, ("baseline for *Bijective (just copying the vector of size n)", V.thaw vrnd >> return ()) | ||
] | ||
|
||
-- | Given a PRNG and a length @n@, generate a random permutation of @[0..n-1]@. | ||
randomPermutationWith :: (StatefulGen g IO) => g -> Int -> IO (V.Vector Int) | ||
randomPermutationWith gen n = do | ||
v <- M.generate n id | ||
V.forM_ (V.generate (n-1) id) $ \ !i -> do | ||
j <- uniformRM (i,n-1) gen | ||
M.swap v i j | ||
V.unsafeFreeze v | ||
|
||
-- | Given @useSize@ benchmark option, compute the largest @n <= 12@ such that @n! <= useSize@. | ||
-- Repeat-nextPermutation-until-end benchmark will use @n@ as the length of the vector. | ||
-- Note that 12 is the largest @n@ such that @n!@ can be represented as an 'Int32'. | ||
useSizeToPermLen :: Int -> Int | ||
useSizeToPermLen us = case V.findIndex (> max 0 us) $ V.scanl' (*) 1 $ V.generate 12 (+1) of | ||
Just i -> i-1 | ||
Nothing -> 12 | ||
|
||
-- | A bijective version of @G.nextPermutation@ that reverses the vector | ||
-- if it is already in descending order. | ||
-- "Bijective" here means that the function forms a cycle over all permutations | ||
-- of the vector's elements. | ||
-- | ||
-- This has a nice property that should be benchmarked: | ||
-- this function takes amortized constant time each call, | ||
-- if successively called either Omega(n) times on a single vector having distinct elements, | ||
-- or arbitrary times on a single vector initially in strictly ascending order. | ||
nextPermutationBijective :: (G.MVector v a, Ord a) => v G.RealWorld a -> IO Bool | ||
nextPermutationBijective v = do | ||
res <- G.nextPermutation v | ||
if res then return True else G.reverse v >> return False | ||
|
||
-- | A bijective version of @G.prevPermutation@ that reverses the vector | ||
-- if it is already in ascending order. | ||
-- "Bijective" here means that the function forms a cycle over all permutations | ||
-- of the vector's elements. | ||
-- | ||
-- This has a nice property that should be benchmarked: | ||
-- this function takes amortized constant time each call, | ||
-- if successively called either Omega(n) times on a single vector having distinct elements, | ||
-- or arbitrary times on a single vector initially in strictly descending order. | ||
prevPermutationBijective :: (G.MVector v a, Ord a) => v G.RealWorld a -> IO Bool | ||
prevPermutationBijective v = do | ||
res <- G.prevPermutation v | ||
if res then return True else G.reverse v >> return False | ||
|
||
-- | Repeat @nextPermutation@ on @[0..n-1]@ until the end. | ||
loopPermutations :: Int -> IO () | ||
loopPermutations n = do | ||
v <- M.generate n id | ||
let loop = do | ||
res <- M.nextPermutation v | ||
if res then loop else return () | ||
loop | ||
|
||
-- | Repeat @prevPermutation@ on @[n-1,n-2..0]@ until the end. | ||
loopRevPermutations :: Int -> IO () | ||
loopRevPermutations n = do | ||
v <- M.generate n (n-1-) | ||
let loop = do | ||
res <- M.prevPermutation v | ||
if res then loop else return () | ||
loop | ||
|
||
-- | Repeat @nextPermutationBijective@ on a given vector given times. | ||
repeatNextPermutation :: V.Vector Int -> Int -> IO () | ||
repeatNextPermutation !v !n = do | ||
!mv <- V.thaw v | ||
let loop !i | i <= 0 = return () | ||
loop !i = do | ||
_ <- nextPermutationBijective mv | ||
loop (i-1) | ||
loop n | ||
|
||
-- | Repeat @prevPermutationBijective@ on a given vector given times. | ||
repeatPrevPermutation :: V.Vector Int -> Int -> IO () | ||
repeatPrevPermutation !v !n = do | ||
!mv <- V.thaw v | ||
let loop !i | i <= 0 = return () | ||
loop !i = do | ||
_ <- prevPermutationBijective mv | ||
loop (i-1) | ||
loop n |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.