Scala使用置换列表进行统一交叉操作的最佳方法是什么?

时间:2011-11-29 15:55:08

标签: list scala permutation genetic-algorithm

我搜索最佳和最优雅的方法,使Scala功能中的GA交叉运算符(No“for”循环,如果可能,只有不可变类型),例如,使用此列表:

val A = IndexedSeq (5,4,8)
val B = IndexedSeq (3,2,6)

我想在我的IndexedSeq中的每个元素之间进行随机比特币排列(例如rng.nextBoolean),最后在元素排列后得到两个列表A'和B'。

执行示例:

rng.nextBoolean <- (true,false,true)
A' = 3,4,6
B' = 5,2,8

感谢。

4 个答案:

答案 0 :(得分:4)

def crossover[T](a: Seq[T], b: Seq[T], rs: Seq[Boolean]) =
  (a, b, rs).zipped.map((x, y, z) => if (z) Seq(x, y) else Seq(y, x)).transpose

与布尔值一起使用作为第三个参数:

scala> val Seq(a1, b1) = crossover(A, B, List(true, false, true))
a1: Seq[Int] = Vector(5, 2, 8)
b1: Seq[Int] = Vector(3, 4, 6)

如果您希望它具有默认的布尔序列,您可以提供如下默认参数:

def crossover[T](a: Seq[T], b: Seq[T], rs: Seq[Boolean] = { 
                                       val rng = new util.Random
                                       Stream.continually(rng.nextBoolean) }) =
  (a, b, rs).zipped.map((x, y, z) => if (z) Seq(x, y) else Seq(y, x)).transpose

答案 1 :(得分:4)

哇,所有这些代码都来自哪里?这里:

val (a1, b1) = A zip B map (t => if (util.Random.nextBoolean) t.swap else t) unzip

那就是全部。

如果您已经有一个随机布尔列表,您可以这样做:

val (a1, b1) = A zip B zip C map { case (t, flag) => if (flag) t.swap else t } unzip

答案 2 :(得分:1)

import scala.util.Random

val A = IndexedSeq(5,4,8)
val B = IndexedSeq(3,2,6)

def crossover[T](rng: Random)(a: Seq[T], b: Seq[T]): (Seq[T],Seq[T]) = {
  if (a.isEmpty && b.isEmpty) return (Nil,Nil)
  val (aTailCrossover,bTailCrossover) = crossover(rng)(a.tail,b.tail)
  if (rng.nextBoolean) (b.head +: aTailCrossover, a.head +: bTailCrossover)
    else               (a.head +: aTailCrossover, b.head +: bTailCrossover)
}

println(crossover(new Random)(A,B))

答案 3 :(得分:0)

def rndCombi [T] (a: Seq[T], b: Seq[T]): Seq[T] = {
  if (a.size != b.size) sys.error ("sizes don't match: a:" + a.size +  " != b: " + b.size) 
  val rnd = util.Random 
  val max = (math.pow (2, a.size)).toInt
  val r = rnd.nextInt (max)

  def pick (a: Seq[T], b: Seq[T], r: Int) : List[T] = {
    if (a.size == 0) Nil else 
    if (r % 2 == 0) a.head :: pick (a.tail , b.tail, r/2) else 
    b.head :: pick (a.tail , b.tail, r/2)
  }

  // print all combinations for testing:
  // (0 until max).map (i => println (pick (a, b, i).mkString ("-")))

  pick (a, b, r).toSeq
}
// I choosed different values for easy testing:
val a = IndexedSeq (7, 8, 9)
val b = IndexedSeq (1, 2, 3)

println (rndCombi (a, b).mkString (" "))
println (rndCombi (a, b.tail).mkString (" "))

如果频繁完成,每次初始化util.Random当然不是很聪明。因此,对于生产代码,您将重新排列代码。

如果不将输入限制为2个序列,则会更有趣。我们走了:

def rndCombi [T] (s: Seq[Seq[T]]): Seq[T] = {

  val outer = s.size 
  val inner = s(0).size 
  val rnd = util.Random 
  val max = (math.pow (outer, inner)).toInt
  val r = rnd.nextInt (max)

  def pick (s: Seq[Seq[T]], r: Int, pos: Int = 0) : List[T] =
    if (pos == inner) Nil 
    else s(r % inner)(pos) :: pick (s, r/inner, pos + 1) 

  // print all combinations for testing:
  (0 until max).map (i => println (pick (s, i).mkString ("-")))  
  println ()
  pick (s, r).toSeq
}

val a = IndexedSeq (1, 2, 3)
val b = IndexedSeq (4, 5, 6)
val c = IndexedSeq (7, 8, 9)

println (rndCombi (Seq (a, b, c)).mkString (" "))

第二种解决方案当然也可用于2个序列。