Бинарный поиск ArrayBuffer

Итак, у меня есть ArrayBuffer[Signal], в котором каждый сигнал имеет временную метку (массив сортируется по этой временной метке). Я хочу выполнить бинарный поиск и вернуть Seq[Signal] сигналов, которые находятся внутри определенного диапазона. Теперь это делается с помощью линейного поиска, в основном потому, что я пришел из Java и новичок в Scala. Как лучше всего это сделать?

Вот код:

private def getSignalsFromCache(mapId: String, mac: String, startTime: Long, endTime: Long): Seq[Signal] = {

val signals = getCache(VehicleWithMap(mapId, mac))
val result: ArrayBuffer[Signal] = new ArrayBuffer[Signal]()

if (signals.isEmpty) {
  return signals
}

var startIndex: Int = 0
if (startTime > signals.head.timestamp) {

  while (startIndex < signals.size && signals(startIndex).timestamp < startTime) {
    startIndex += 1
  }
}

var finished: Boolean = false
var currentIndex = startIndex
while (!finished && currentIndex < signals.size) {
  val timestamp = signals(currentIndex).timestamp
  if (timestamp > endTime) {
    finished = true
  }
  else {
    result += signals(currentIndex)
  }
  currentIndex += 1
}
result
}

Насколько я понимаю, ArrayBuffer сортируется по возрастанию, верно?

Luis Miguel Mejía Suárez 21.03.2019 16:12

@LuisMiguelMejíaSuárez да, это так

Guillem 21.03.2019 16:54

Это кажется простым. Замените startIndex+=1 на startIndex+=half_the_unchecked_length_in_the_correct_directi‌​on(). Это просто вопрос бухгалтерии.

AShelly 21.03.2019 18:04
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
3
202
3

Ответы 3

Вы можете использовать Searching для выполнения бинарного поиска.

import scala.collection.Searching._

val signalWithLowerBoundTimestamp = ???
val signalWithUpperBoundTimestamp = ???
val lower = signals.search(signalWithLowerBoundTimestamp)(
    Ordering.by(_.timestamp)).insertionPoint
// If the upper bound is an exact match, add one
// to make sure it's included in the slice.
val upper = signals.search(signalWithUpperBoundTimestamp)(
    Ordering.by(_.timestamp)) match {
  case Found(i) => i + 1
  case InsertionPoint(i) => i
}
val result = signals.slice(lower, upper)

Ordering.by(_.timestamp) не разрешен IDE

Guillem 22.03.2019 11:32

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

Matt Timmermans 22.03.2019 13:42

@Guillem, вы можете сказать мне, какая ошибка произошла? Может быть, вам нужно импортировать Ordering?

Brian McCutchon 22.03.2019 16:44

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

Brian McCutchon 22.03.2019 16:48

@BrianMcCutchon Дело в том, что я очень новичок в Scala. Сигнал - это класс case, поэтому, когда я делаю signal.search(startTime)(Ordering.by(_.timestamp)) IDE не может разрешить, потому что не определен специальный порядок. Мой класс case такой: класс case Signal(ap: AccessPoint, timestamp: Long, rssi: Double, mac: String = ""). Как мне определить порядок и как с его помощью вызвать поиск метода? Спасибо, Брайан, ты классный!

Guillem 25.03.2019 17:45

Вы можете использовать dropWhile и takeWhile.
Таким образом, вы можете сохранить все изменения и итерации.

Примечание: Это по-прежнему линейно, но более функционально и более распространено в Скала.

private def getSignalsFromCache(mapId: String, mac: String, startTime: Long, endTime: Long): Seq[Signal] =
  getCache(VehicleWithMap(mapId, mac)
    .dropWhile(_.timestamp < startTime)
    .takeWhile(_.timestamp <= endTime)
}

(не забудьте сначала протестировать его, возможно, вам придется немного поиграть с условиями).

Я не знаю scala, но вот бинарный поиск, с которым вы можете сравнить более функциональные ответы. Я не думаю, что в этом есть что-то постыдное. Обратите внимание, что нет смысла выполнять бинарный поиск по конечному индексу, поскольку вам все равно придется копировать элементы:

private def getSignalsFromCache(mapId: String, mac: String, startTime: Long, endTime: Long): Seq[Signal] = {

    val signals = getCache(VehicleWithMap(mapId, mac))

    var startIndex: Int = 0
    var endIndex: Int = signals.size

    while(startIndex<endIndex) {
        var testIndex: int = startIndex + (endIndex-startIndex)/2
        if (signals(testIndex).timestamp < startTime) {
            startIndex = testIndex + 1
        } else {
            endIndex = testIndex
        }
    }

    while(endIndex < signals.size && signals(endIndex).timestamp <= endTime) {
        endIndex = endIndex + 1
    }
    signals.slice(startIndex, endIndex)
}

Обратите внимание, что я включил сигналы в конце с помощью timestamp == endTime, потому что это то, что вы сделали... хотя это делает интерфейс немного странным. Обычно такой метод должен быть написан для возврата сигналов с помощью startTime <= timeStamp < endTime, поэтому, возможно, вы захотите подумать об этом.

Другие вопросы по теме