Ближайшие ключи в SortedMap - PullRequest
       0

Ближайшие ключи в SortedMap

15 голосов
/ 30 августа 2011

Учитывая ключ k в SortedMap, как мне эффективно найти самый большой ключ m, который меньше или равен k, а также самый маленький ключ n, который больше или равен k. Спасибо.

Ответы [ 8 ]

8 голосов
/ 30 августа 2011

Глядя на исходный код для 2.9.0, следующий код кажется наилучшим, что вы можете сделать

def getLessOrEqual[A,B](sm: SortedMap[A,B], bound: A): B = {
  val key = sm.to(x).lastKey
  sm(key)
}

Я не знаю точно, как работает расщепление дерева RedBlack, но я предполагаю, что это что-то вроде обхода O (log n) дерева / построения новых элементов, а затем балансировки, предположительно также O (log n) ). Затем вам нужно снова спуститься по новому дереву, чтобы получить последний ключ. К сожалению, вы не можете получить значение за один раз. Поэтому вам нужно снова пойти вниз, чтобы получить значение.

Кроме того, lastKey может вызвать исключение, и нет аналогичного метода, который возвращает Option.

Я жду исправлений.

Редактирование и личный комментарий

Область SortedMap библиотеки std, похоже, немного игнорируется. Мне также не хватает изменяемой SortedMap. Просматривая источники, я заметил, что отсутствуют некоторые важные методы (например, тот, о котором просит OP, или те, которые указаны в моем ответе), а также некоторые имеют плохую реализацию, например, 'last', который определяется TraversableLike и используется через полное дерево от первого до последнего, чтобы получить последний элемент.

Редактировать 2

Теперь вопрос переформулирован, и мой ответ больше не действителен (ну, в любом случае, раньше этого не было). Я думаю, что вы должны сделать то, что я описываю дважды, для lessOrEqual и moreOrEqual. Ну, вы можете воспользоваться ярлыком, если найдете равный элемент.

3 голосов
/ 28 декабря 2015

Используя Scala 2.11.7, вы получите то, что вам нужно:Если вы хотите немного повысить эффективность, попробуйте заменить from(...).head на keysIteratorFrom(...).head

3 голосов
/ 30 августа 2011

У черты SortedSet в Scala нет метода, который бы дал вам самый близкий элемент к какому-либо другому элементу.

В настоящее время он реализован с TreeSet, который основан на RedBlack.Дерево RedBlack не отображается через методы в TreeSet, но защищенный метод tree защищен.К сожалению, это в принципе бесполезно.Вам придется переопределить методы, возвращающие TreeSet, чтобы вернуть ваш подкласс, но большинство из них основаны на newSet, который является частным.

Итак, в конце концов, вам придется дублировать большинствоTreeSet.С другой стороны, это не так уж много кода.

Как только вы получите доступ к RedBlack, вам придется реализовать что-то похожее на RedBlack.Tree 's lookup, так что вы'иметь производительность O(logn).Это на самом деле та же сложность, что и range, хотя она, безусловно, будет выполнять меньше работы.

В качестве альтернативы вы бы сделали молнию для дерева, чтобы вы могли фактически перемещаться по набору за постоянное время.Конечно, было бы гораздо больше работы.

2 голосов
/ 11 июня 2015

К сожалению, библиотека Scala позволяет только эффективно выполнять этот тип запроса:

, а также самый маленький ключ n, который больше или равен k.

val n = TreeMap(...).keysIteratorFrom(k).next

Вы можете взломать это, сохранив две структуры: одну с обычными ключами, а другую с отрицательными ключами.Затем вы можете использовать другую структуру для выполнения второго типа запроса.

val n = - TreeMap(...).keysIteratorFrom(-k).next
1 голос
/ 08 апреля 2016

У меня была похожая проблема: я хотел найти ближайший элемент к данному ключу в SortedMap.Я помню ответ на этот вопрос: «Вы должны взломать TreeSet», поэтому, когда мне пришлось реализовать его для проекта, я нашел способ обернуть TreeSet, не вдаваясь в его внутренности.

Я не видел ответа Джазмита, который более точно отвечает на вопрос автора плаката с минимальной суетой (два вызова метода).Тем не менее, эти вызовы методов выполняют больше работы, чем необходимо для этого приложения (несколько обходов дерева), и мое решение предоставляет множество хуков, где другие пользователи могут изменять его в соответствии со своими потребностями.

Вот оно:

import scala.collection.immutable.TreeSet
import scala.collection.SortedMap

// generalize the idea of an Ordering to metric sets
trait MetricOrdering[T] extends Ordering[T] {
  def distance(x: T, y: T): Double
  def compare(x: T, y: T) = {
    val d = distance(x, y)
    if (d > 0.0) 1
    else if (d < 0.0) -1
    else 0
  }
}

class MetricSortedMap[A, B]
  (elems: (A, B)*)
  (implicit val ordering: MetricOrdering[A])
  extends SortedMap[A, B] {

  // while TreeSet searches for an element, keep track of the best it finds
  // with *thread-safe* mutable state, of course
  private val best = new java.lang.ThreadLocal[(Double, A, B)]
  best.set((-1.0, null.asInstanceOf[A], null.asInstanceOf[B]))

  private val ord = new MetricOrdering[(A, B)] {
    def distance(x: (A, B), y: (A, B)) = {
      val diff = ordering.distance(x._1, y._1)
      val absdiff = Math.abs(diff)

      // the "to" position is a key-null pair; the object of interest
      // is the other one
      if (absdiff < best.get._1)
        (x, y) match {
          // in practice, TreeSet always picks this first case, but that's
          // insider knowledge
          case ((to, null), (pos, obj)) =>
            best.set((absdiff, pos, obj))

          case ((pos, obj), (to, null)) =>
            best.set((absdiff, pos, obj))

          case _ =>
        }

      diff
    }
  }

  // use a TreeSet as a backing (not TreeMap because we need to get
  // the whole pair back when we query it)
  private val treeSet = TreeSet[(A, B)](elems: _*)(ord)

  // find the closest key and return:
  // (distance to key, the key, its associated value)
  def closest(to: A): (Double, A, B) = {
    treeSet.headOption match {
      case Some((pos, obj)) =>
        best.set((ordering.distance(to, pos), pos, obj))
      case None =>
        throw new java.util.NoSuchElementException(
          "SortedMap has no elements, and hence no closest element")
    }

    treeSet((to, null.asInstanceOf[B]))  // called for side effects

    best.get
  }

  // satisfy the contract (or throw UnsupportedOperationException)
  def +[B1 >: B](kv: (A, B1)): SortedMap[A, B1] =
    new MetricSortedMap[A, B](
      elems :+ (kv._1, kv._2.asInstanceOf[B]): _*)
  def -(key: A): SortedMap[A, B] =
    new MetricSortedMap[A, B](elems.filter(_._1 != key): _*)
  def get(key: A): Option[B] = treeSet.find(_._1 == key).map(_._2)
  def iterator: Iterator[(A, B)] = treeSet.iterator
  def rangeImpl(from: Option[A], until: Option[A]): SortedMap[A, B] =
    new MetricSortedMap[A, B](treeSet.rangeImpl(
      from.map((_, null.asInstanceOf[B])),
      until.map((_, null.asInstanceOf[B]))).toSeq: _*)
}

// test it with A = Double
implicit val doubleOrdering =
  new MetricOrdering[Double] {
    def distance(x: Double, y: Double) = x - y
  }

// and B = String
val stuff = new MetricSortedMap[Double, String](
  3.3 -> "three",
  1.1 -> "one",
  5.5 -> "five",
  4.4 -> "four",
  2.2 -> "two")

println(stuff.iterator.toList)

println(stuff.closest(1.5))
println(stuff.closest(1000))
println(stuff.closest(-1000))
println(stuff.closest(3.3))
println(stuff.closest(3.4))
println(stuff.closest(3.2))
1 голос
/ 11 июня 2015

Ну, один вариант, безусловно, использует java.util.TreeMap.

У него есть методы lowerKey и higherKey, которые делают то, что вам нужно.

1 голос
/ 30 августа 2011

Похоже, я должен подать заявку на добавление методов fromIterator и toIterator к признаку Sorted.

0 голосов
/ 28 мая 2013

Я делал:

val m = SortedMap(myMap.toSeq:_*)
val offsetMap = (m.toSeq zip m.keys.toSeq.drop(1)).map {
  case ( (k,v),newKey) => (newKey,v)
}.toMap

Когда я хочу, чтобы результаты моей карты были смещены одним ключом.Я также ищу лучший способ, желательно без сохранения дополнительной карты.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...