Nano Hash - криптовалюты, майнинг, программирование

Scala: идиоматический способ объединить список карт с наибольшим значением каждого ключа?

У меня есть список карт [Int, Int], все они имеют одинаковые ключи (от 1 до 20), и я хотел бы объединить их содержимое в одну карту [Int, Int].

Я прочитал еще один пост о переполнении стека о слиянии карт. который использует |+| из библиотеки scalaz.

Я придумал следующее решение, но оно кажется мне неуклюжим.

val defaultMap = (2 to ceiling).map((_,0)).toMap
val factors: Map[Int, Int] = (2 to ceiling). map(primeFactors(_)).
        foldRight(defaultMap)(mergeMaps(_, _))

def mergeMaps(xm: Map[Int, Int], ym: Map[Int, Int]): Map[Int,Int] = {
    def iter(acc: Map[Int,Int], other: Map[Int,Int], i: Int): Map[Int,Int] = {
      if (other.isEmpty) acc
      else iter(acc - i + (i -> math.max(acc(i), other(i))), other - i, i + 1)
    }
    iter(xm, ym, 2)
  }

def primeFactors(number: Int): Map[Int, Int] = {
  def iter(factors: Map[Int,Int], rem: Int, i: Int): Map[Int,Int] = {
    if (i > number) factors
    else if (rem % i == 0) iter(factors - i + (i -> (factors(i)+1)), rem / i, i)
    else iter(factors, rem, i + 1)
  }
  iter((2 to ceiling).map((_,0)).toMap, number, 2)
}

Объяснение: val factors создает список карт, каждая из которых представляет простые множители для чисел от 2 до 20; затем эти 18 карт складываются в одну карту, содержащую наибольшее значение для каждого ключа.

ОБНОВИТЬ

Используя предложение @folone, я получаю следующий код (определенное улучшение по сравнению с моей исходной версией, и мне не нужно менять карты на HashMaps):

import scalaz._
import Scalaz._
import Tags._

/**
 * Smallest Multiple
 *
 * 2520 is the smallest number that can be divided by each of the numbers 
 * from 1 to 10 without any remainder. What is the smallest positive number 
 * that is evenly divisible by all of the numbers from 1 to 20?
 *
 * User: Alexandros Bantis
 * Date: 1/29/13
 * Time: 8:07 PM
 */
object Problem005 {

  def findSmallestMultiple(ceiling: Int): Int = {
    val factors = (2 to ceiling).map(primeFactors(_).mapValues(MaxVal)).reduce(_ |+| _)
    (1 /: factors.map(m => intPow(m._1, m._2)))(_ * _)
  }

  private def primeFactors(number: Int): Map[Int, Int] = {
    def iter(factors: Map[Int,Int], rem: Int, i: Int): Map[Int,Int] = {
      if (i > number) factors.filter(_._2 > 0).mapValues(MaxVal)
      else if (rem % i == 0) iter(factors - i + (i -> (factors(i)+1)), rem / i, i)
      else iter(factors, rem, i + 1)
    }
    iter((2 to number).map((_,0)).toMap, number, 2)
  }

  private def intPow(x: Int, y: Int): Int = {
    def iter(acc: Int, rem: Int): Int = {
      if (rem == 0) acc
      else iter(acc * x, rem -1)
    }
    if (y == 0) 1 else iter(1, y)
  }
}

Ответы:


1

Как следует из ваших тегов, вас может заинтересовать решение scalaz. Вот оно:

> console
[info] Starting scala interpreter...
[info] 
Welcome to Scala version 2.10.0 (OpenJDK 64-Bit Server VM, Java 1.7.0_15).
Type in expressions to have them evaluated.
Type :help for more information.

scala> import scalaz._, Scalaz._, Tags._
import scalaz._
import Scalaz._
import Tags._

Существует экземпляр Semigroup для Ints при максимальной операции:

scala> Semigroup[Int @@ MaxVal]
res0: scalaz.Semigroup[scalaz.@@[Int,scalaz.Tags.MaxVal]] = scalaz.Semigroup$$anon$9@15a9a9c6

Давайте просто используем его:

scala> val m1 = Map(1 -> 2, 2 -> 3) mapValues MaxVal
m1: scala.collection.immutable.Map[Int,scalaz.@@[Int,scalaz.Tags.MaxVal]] = Map(1 -> 2, 2 -> 3)

scala> val m2 = Map(1 -> 3, 4 -> 5) mapValues MaxVal
m2: scala.collection.immutable.Map[Int,scalaz.@@[Int,scalaz.Tags.MaxVal]] = Map(1 -> 3, 4 -> 5)

scala> m1 |+| m2
res1: scala.collection.immutable.Map[Int,scalaz.@@[Int,scalaz.Tags.MaxVal]] = Map(1 -> 3, 4 -> 5, 2 -> 3)

Если вам интересно, как работает эта «маркировка» (вещь @@), вот хорошее объяснение: http://etorreborre.blogspot.de/2011/11/practical-uses-for-unboxed-tagged-types.html

28.02.2013

2

Это решение не работает для обычных Map, но если вы используете immutable.HashMap, вы можете рассмотреть mergedметод:

def merged[B1 >: B](that: HashMap[A, B1])(mergef: ((A, B1), (A, B1)) ⇒ (A, B1)): HashMap[A, B1]

Создает новую карту, которая представляет собой слияние этой и хеш-карты аргумента.

Использует указанную функцию разрешения коллизий, если два ключа совпадают. Функция разрешения коллизий всегда будет принимать первый аргумент из этой хеш-карты, а второй — из нее.

Метод слияния в среднем более эффективен, чем выполнение обхода и воссоздание новой неизменяемой хеш-карты с нуля или ++.

Вариант использования:

val m1 = immutable.HashMap[Int, Int](1 -> 2, 2 -> 3)
val m2 = immutable.HashMap[Int, Int](1 -> 3, 4 -> 5)
m1.merged(m2) {
  case ((k1, v1), (k2, v2)) => ((k1, math.max(v1, v2)))
}
27.02.2013

3

Начиная с Scala 2.13 другое решение, основанное только на стандартной библиотеке, заключается в объединении Map в виде последовательностей перед применением groupMapReduce, который (как следует из его названия) является эквивалентом groupBy, за которым следует сопоставление и шаг уменьшения значений:

// val map1 = Map(1 -> 2, 2 -> 3)
// val map2 = Map(1 -> 3, 4 -> 5)
(map1.toSeq ++ map2).groupMapReduce(_._1)(_._2)(_ max _)
// Map[Int,Int] = Map(2 -> 3, 4 -> 5, 1 -> 3)

Этот:

  • объединяет две карты в виде последовательности кортежей (List((1,2), (2,3), (1,3), (4,5))). Для краткости map2 неявно преобразуется в Seq, чтобы принять тип map1.toSeq, но вы можете сделать его явным, используя map2.toSeq.

  • groups элементов на основе их первой части кортежа (групповая часть groupMapReduce)

  • maps сгруппировали значения во вторую часть кортежа (часть сопоставления группы MapReduce)

  • reduces сопоставленных значений (_ max _), взяв их максимум (уменьшить часть groupMapReduce)

04.02.2019
Новые материалы

Кластеризация: более глубокий взгляд
Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

Как написать эффективное резюме
Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

Частный метод Python: улучшение инкапсуляции и безопасности
Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

Как я автоматизирую тестирование с помощью Jest
Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..