Scala лучший способ сделать единую кроссоверную операцию с перестановкой?
Я ищу самый лучший и самый элегантный способ сделать оператор кроссовера GA в функциональном Scala функционале (нет "для" цикла, с возможным только неизменным типом), например, с помощью этого списка:
val A = IndexedSeq (5,4,8)
val B = IndexedSeq (3,2,6)
Я хочу сделать произвольную перестановку биткойнов (например, rng.nextBoolean
) между каждым элементом в моем IndexedSeq и, наконец, я получу два списка A 'и B' после перестановки своих элементов.
Пример выполнения:
rng.nextBoolean <- (true,false,true)
A' = 3,4,6
B' = 5,2,8
Спасибо.
Ответы
Ответ 1
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)
Если вы хотите его с последовательностью по умолчанию Booleans, вы можете указать аргумент по умолчанию, например:
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
Ответ 2
Ничего себе, откуда весь этот код? Здесь:
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
Ответ 3
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))
Ответ 4
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 (" "))
Второе решение, конечно же, можно использовать и для двух последовательностей.