diff --git a/list7/src/BestSolution.scala b/list7/src/BestSolution.scala new file mode 100644 index 00000000..33e32710 --- /dev/null +++ b/list7/src/BestSolution.scala @@ -0,0 +1,67 @@ +import ParallelResearchUtils.* + +object BestSolution { + //wyszukiwanie najlepszego rozwiązania w populacji + // tablica zawierająca ciągi binarne jest przeszukiwana w celu znalezienia osobnika o najlepszym przystosowaniu (najwięcej 1) + // rozwiązanie zrównoleglone jest lepsze dla populacji większych niż 10 000 osobników, próg zrównoleglenia 1000 + // wyniki dla powyższych danych: algorytm rónwoległy dwa razy lepszy (156ms do 328ms) + + + def generateSeriesArray(size: Int ): Array[String] = { + var counter = 0 + var array = Array[String]() + while(counter < size){ + array = array :+ generateBinSeries(10000) + counter += 1 + } + array + } + + def generateBinSeries(length: Int): String ={ + var series = "" + var count = 0 + val rand = scala.util.Random() + while( count < length){ + if rand.nextDouble() < 0.5 then series = series + "0" + else series = series + "1" + count += 1 + } + series + } + + def quality(series: String): Int ={ + var index = 0 + var quality = 0 + while(index < series.length){ + if(series.charAt(index) == '1') then quality += 1 + index += 1 + } + quality + } + def findBestSolutionHelper(wholePopulation: Array[String], start: Int, end: Int): String ={ + if(end - start == 1) then + if quality(wholePopulation(start)) < quality(wholePopulation(end)) then wholePopulation(end) + else wholePopulation(start) + else + val partition = (start + end )/2 + val f1 = findBestSolutionHelper(wholePopulation, start, partition) + val f2 = findBestSolutionHelper(wholePopulation, partition, end) + if(quality(f1) < quality(f2)) then f2 + else f1 + } + def findBestSolution(population: Array[String]): String ={ + findBestSolutionHelper(population, 0, population.size - 1) + } + + def findBestSolutionParallel(population: Array[String]): String ={ + def findBestSolutionHelperParallel(wholePopulation: Array[String], start: Int, end: Int): String ={ + if(end - start < 1000) then findBestSolutionHelper(wholePopulation, start, end) + else + val partition = (start + end )/2 + val (r1, r2) = parallel(findBestSolutionHelperParallel(wholePopulation, start, partition), findBestSolutionHelperParallel(wholePopulation, partition, end)) + if(quality(r1) < quality(r2)) then r2 + else r1 + } + findBestSolutionHelperParallel(population, 0, population.size - 1) + } +} diff --git a/list7/src/BinTreeOperations.scala b/list7/src/BinTreeOperations.scala new file mode 100644 index 00000000..ed7c92b2 --- /dev/null +++ b/list7/src/BinTreeOperations.scala @@ -0,0 +1,72 @@ +import ParallelResearchUtils.* + +object BinTreeOperations { + + // Przy klasycznym liczeniu sumy elementów drzewa drzewa binarnego zdecydowana przewaga rozwiązania jednowątkowego + // dla głębokości 15 program zrównoleglony osiagnął wynik 78ms, niezrównoleglony 0ms + // w celu sztucznego wydłużenia obliczeń z każdego elementu liczyłam silnię, + // co przy dużych liczbach i drzewach o głębokości większej od 10 dawało bardzo niewielką przewagę algorytmowi równoległemu + // głebokośc 15, np. równoległy 1266ms, zwykły 1281ms + // problem raczej nie nadaje się do rozwiązania równoległego + + sealed trait BinTree[+A] + case object Empty extends BinTree[Nothing] + case class Node[+A](elem: A, left: BinTree[A], right: BinTree[A]) extends BinTree[A] + + val rand = scala.util.Random() + + def generateTreeOfDepth(depth: Int): BinTree[Int] = { + if depth <= 0 then Empty + else + Node(Math.abs(rand.nextInt()) % 100000, generateTreeOfDepth(depth - 1), generateTreeOfDepth(depth - 1)) + } + + def sumOfTreeFactorial(tree: BinTree[Int]): Int ={ + tree match{ + case Node(v, left, right) => factorial(v) + factorial(sumOfTree(left))+ factorial(sumOfTree(right)) + case Empty => 0 + } + } + + def sumOfTreeFactorialParallel(tree: BinTree[Int], depthOfTree: Int): Int = { + if depthOfTree < 10 then sumOfTree(tree) + else + tree match{ + case Node(v, left, right) => + val result = parallel(sumOfTreeParallel(left, depthOfTree - 1), sumOfTreeParallel(right, depthOfTree - 1)) + (factorial(v) + factorial(result._1) + factorial(result._2)) + case Empty => 0 + } + } + + def sumOfTree(tree: BinTree[Int]): Int ={ + tree match{ + case Node(v, left, right) => v + sumOfTree(left)+ sumOfTree(right) + case Empty => 0 + } + } + + def sumOfTreeParallel(tree: BinTree[Int], depthOfTree: Int): Int = { + if depthOfTree < 10 then sumOfTree(tree) + else + tree match{ + + case Node(v, left, right) => + val result = parallel(sumOfTreeParallel(left, depthOfTree - 1), sumOfTreeParallel(right, depthOfTree - 1)) + (v + result._1 + result._2) + case Empty => 0 + } + } + + def factorial(number: Int): Int = { + def factorialHelper(numberTail: Int, acc: Int): Int = { + numberTail match { + case 0 => 1 + case 1 => 1 + case _ => factorialHelper (numberTail - 1, acc * numberTail) + } + } + if number < 0 then throw new IllegalArgumentException() + else factorialHelper(number, 1) + } +} diff --git a/list7/src/Main.scala b/list7/src/Main.scala new file mode 100644 index 00000000..971268f7 --- /dev/null +++ b/list7/src/Main.scala @@ -0,0 +1,43 @@ +import MergeSort.* +import BinTreeOperations.* +import BestSolution.* +import QuickSort.* + +import ParallelResearchUtils.* +import scala.concurrent.{Await, Future} +import scala.concurrent.duration.Duration + +object Main { + def main(args: Array[String]): Unit = { +// val toSort = generateList(50000, Nil) +// +// println(timer(mergesort(isLess, toSort))) +// +// println(timer(mergesortParallel(isLess, toSort))) + + +// val intTree = generateTreeOfDepth(15) +// +// println(timer(sumOfTree(intTree))) +// println(timer(sumOfTreeParallel(intTree, 15))) +// +// println(timer(sumOfTreeFactorial(intTree))) +// println(timer(sumOfTreeFactorialParallel(intTree, 15))) +// +// + //val array = Array("0101", "1011", "0111", "1011") + val arraySeries = generateSeriesArray(10000) + println("done") + + //println(array.toList) + println(timer(findBestSolution(arraySeries))) + println(timer(findBestSolutionParallel(arraySeries))) +// + +// val array = generateArray(800000) +// val array2 = array.clone() +// val array1 = array.clone() +// println(timer(quicksort(array1))) +// println(timer(quicksortParallel(array2))) + } +} diff --git a/list7/src/MergeSort.scala b/list7/src/MergeSort.scala new file mode 100644 index 00000000..a5d74b4c --- /dev/null +++ b/list7/src/MergeSort.scala @@ -0,0 +1,62 @@ +import scala.annotation.tailrec +import ParallelResearchUtils.* + +object MergeSort { + // Widoczna przewaga zrównoleglonego rozwiązania od 100 000 danych przy progu 50 000 - czas dwa razy krótszy (78ms w porównaniu do 141ms) + // dla mniejszych danych wyniki algorytmów są porównywalne dla 50 000 danych przy progu 10 000 (63ms dla równoległego, 78ms dla zwykłego) + // przy mniejszych lepsze rozwiązanie jednowątkowe + + val lengthThreshold = 10000 + val rand = scala.util.Random() + + def generateList(length: Int, acc: List[Int]): List[Int] = { + if length < 0 then throw new IllegalArgumentException() + else if length == 0 then acc + else generateList(length - 1, rand.nextInt() :: acc) + } + val isLess : (Int, Int) => Boolean = (arg1, arg2) => (arg1 < arg2) + + def splitList[A](list : List[A], partition : Int) : (List[A], List[A]) ={ + @tailrec + def splitIter(sublists :(List[A], List[A]), counter : Int) : (List[A], List[A]) ={ + if counter == 0 then (sublists._1.reverse, sublists._2) + else splitIter((sublists._2.head :: sublists._1, sublists._2.tail), counter - 1) + } + splitIter((Nil, list), partition) + } + + def merge[A](order: (A, A) => Boolean, listLeft: List[A], listRight: List[A]): List[A] = { + def mergeHelper(lleft: List[A], lright: List[A], acc: List[A]): List[A] = { + (lleft, lright) match + case (Nil, _) => lright.reverse ::: acc + case (_, Nil) => lleft.reverse ::: acc + case (h1 :: t1 , h2 :: t2) => + if order(h2, h1) then mergeHelper(lleft, t2, h2 :: acc) + else mergeHelper(t1, lright, h1 :: acc) + } + mergeHelper(listLeft, listRight, Nil).reverse + } + + def mergesort[A](order: (A, A) => Boolean,list : List[A]) : List[A] = { + val partition = list.length / 2 + if partition == 0 then list + else { + val (left, right): (List[A], List[A]) = splitList(list, partition) + merge(order, mergesort(order, left), mergesort(order, right)) + } + } + + def mergesortParallel[A](order: (A, A) => Boolean,list : List[A]) : List[A] ={ + val length = list.length + val partition = length / 2 + if length < 2 then list + else { + val (left, right): (List[A], List[A]) = splitList(list, partition) + if length < lengthThreshold then merge(order, left, right) + else { + val (r1, r2): (List[A], List[A]) = parallel(mergesortParallel(order, left), mergesortParallel(order, right)) + merge(order, r1, r2) + } + } + } + } diff --git a/list7/src/ParallelResearchUtils.scala b/list7/src/ParallelResearchUtils.scala new file mode 100644 index 00000000..564eafef --- /dev/null +++ b/list7/src/ParallelResearchUtils.scala @@ -0,0 +1,19 @@ +import scala.concurrent.{Await, Future} +import scala.concurrent.duration.Duration +import scala.concurrent.ExecutionContext.Implicits.global + +object ParallelResearchUtils { + def parallel[A, B](taskA: => A, taskB: => B): (A,B) = { + val future: Future[B] = Future { + taskB + } + val a: A = taskA + val b: B = Await.result(future, Duration.Inf) + (a, b) + } + def timer[A](task: => A): Long ={ + val start = System.currentTimeMillis() + task + System.currentTimeMillis() - start + } +} diff --git a/list7/src/QuickSort.scala b/list7/src/QuickSort.scala new file mode 100644 index 00000000..1f0e137c --- /dev/null +++ b/list7/src/QuickSort.scala @@ -0,0 +1,65 @@ +import ParallelResearchUtils.* + +object QuickSort { + // Czas sortowania zrównoleglonego był niższy tylko dla bardzo dużej liczby danych wejściowych ok.700 000, próg zrównoleglenia 300 000, + // przykładowe wyniki dla 800000 danych: równoległy: 78ms, nierównoległy 94 ms + // dla mniejszych wartości zdecydowana przewaga jednowątkowego rozwiązania + + def generateArray(length: Int): Array[Int] ={ + var array = Array[Int]() + val rand = scala.util.Random() + var currentLength = 0 + while(currentLength < length){ + array = array :+ rand.nextInt() + currentLength += 1 + } + array + } + + def swap(tab: Array[Int])(i: Int)(j: Int) : Unit = + var aux = tab(i) + tab(i) = tab(j) + tab(j) = aux + + def choose_pivot(tab: Array[Int])(m: Int)(n: Int): Int = tab((m + n)/2) + + def partition(tab: Array[Int])(l: Int)(r: Int): (Int, Int) = + var i = l + var j = r + var pivot = choose_pivot(tab)(l)(r) + while i <= j do + while tab(i) < pivot do i += 1; + while pivot < tab(j) do j -= 1; + if i <= j then + swap(tab)(i)(j); + i += 1; + j -= 1; + (i, j) + + + def quick(tab: Array[Int])(l: Int)(r: Int): Unit = + if l < r then + var (i, j) = partition(tab)(l)(r) + if j - l < r - i then + quick(tab)(l)(j) + quick(tab)(i)(r) + else + quick(tab)(i)(r) + quick(tab)(l)(j) + + def quicksort(tab: Array[Int]): Unit = + quick(tab)(0)(tab.length - 1) + + def quickParallel(tab: Array[Int])(l: Int)(r: Int): Unit = + if l < r then + if r-l < 300000 then quick(tab)(l)(r) + else + var (i, j) = partition(tab)(l)(r) + if j - l < r - i then + parallel(quickParallel(tab)(l)(j), quickParallel(tab)(i)(r)) + else + parallel(quickParallel(tab)(i)(r), quickParallel(tab)(l)(j)) + + def quicksortParallel(tab: Array[Int]): Unit = + quickParallel(tab)(0)(tab.length - 1) +}