Опишите алгоритм для нахождения миллиона наименьших чисел в наборе из миллиарда чисел. Память

Алгоритм нахождения простых чисел (стр. 1 из 2)

Индийские математики нашли уникальный алгоритм поиска простых чисел

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

Простые числа — это ключ к разрешению многих математических проблем, они также играют большую роль в криптографии (шифровании), благодаря чему интересуют не только математиков, но и военных, разведку и контрразведку. Простое число — то, которое делится без остатка только на единицу и на само себя. Так, к простым числам относятся 2, 3, 5, 7, 11, 13 и так далее по возрастающей.

Первым проблему определения простых чисел поставил древнегреческий ученый Эратосфен примерно в 220 году до нашей эры, предложив один из путей определения простых чисел. С тех пор ученые постепенно продвигались вперед, а в последние десятилетия им на помощь в проверке делимости огромных чисел пришли компьютеры. Математики, а позже и специалисты по компьютерному программированию разработали много способов решения этой проблемы, однако все они несут небольшую потенциальную возможность ошибки.

«Наш алгоритм исключает вероятность любой ошибки», — заявил основной разработчик нового метода Маниндра Агравал. Результаты вычислений уже разосланы ведущим компьютерным специалистам и математикам во всем мире. Ученые еже получили несколько отзывов. Никто не высказывает сомнений в новом алгоритме, и все выражают удовлетворение достигнутым результатом, сообщает NTVRU.com.

Эратосфен Киренский (276 год до н.э -194 год до н.э.) – греческий математик, астроном, географ и поэт. Ученик Каллимаха, с 235 год до н.э. – глава Александрийской библиотеки.

Эратосфен-Сын Эглаоса, уроженец Кирены.

Начальное образование Эратосфен получил в Александрии под руководством своего учёного земляка Каллимаха. Другим учителем Эратосфена в Александрии был философ Лизний. Перебравшись затем в Афины, он так тесно сблизился со школой Платона, что обыкновенно называл себя платоником. Результатом изучения наук в этих двух центрах была энциклопедическая эрудиция Эратосфена; кроме сочинений по математическим наукам, он писал ещё трактаты «О добре и зле», о комедии и др. Из всех своих сочинений Эратосфен придавал особенное значение чисто литературным и грамматическим, как это можно заключить из того, что он любил называть себя филологом.

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

Отголоски призвания обширной учёности Эратосфена звучат и в прозвищах, которые он получил от современников. Называя его «бета», они, по предположению многих исследователей, желали выразить свой взгляд на него, как на второго Платона, или вообще как на учёного, который только потому занимает второе место, что первое должно быть удержано за предками. Другим прозвищем Эратосфена было «пентатл»-пятиборец.

В честь Эратосфена назван кратер на Луне.

Решето Эратосфена-алгоритм нахождения всех простых чисел до некоторого целого числа n, который приписывают древнегреческому математику Эратосфену Киренскому

Для нахождения всех простых чисел не больше заданного числа n, следуя методу Эратосфена, нужно выполнить следующие шаги:

1) Выписать подряд все целые числа от 2 до n (2,3,4…,n)

2) Пусть переменная p изначально равна 2-первому простому числу.

3) Вычеркнуть из списка все числа от 2p до n, делящиеся на p (то есть, числа 2p,3p,4p,… .)

4) Найти первое невычеркнутое число, большее, чем р, и присвоить значению переменной p это число.

5) Повторять шаги 3 и 4 до тех пор, пока p не станет больше, чем n.

6) Все невычеркнутые числа в списке — простые числа.

На практике, алгоритм можно немного улучшить следующим образом. На шаге №3, числа можно вычеркивать, начиная сразу с числа p 2 , потому что все составные числа меньше его уже будут вычеркнуты к этому времени. И, соответственно, останавливать алгоритм можно, когда p 2 станет больше, чем n.

Число делится на 2 тогда и только тогда когда оно заканчивается чётной цифрой или нулём.

Число делится на 3, когда сумма цифр числа делится на 3.

Число делится на 4 тогда и только тогда, когда число из двух его последних цифр (оно может быть двузначным, однозначным или нулём) делится на 4.

Чтобы узнать, делится ли двузначное число на 4, можно половину единиц прибавить к десяткам — если сумма делится на 2, значит, число делится на 4.

Например, 92: 9+1=10, значит, 92 делится на 4.

Число делится на 5, когда оно заканчивается на 0 или 5.

Число делится на 6 тогда и только тогда, когда оно делится и на 2, и на 3.

Число делится на 7 когда результат вычитания удвоенной последней цифры, из этого числа без последней цифры делится на 7.

Например, 343:34-3*2=28 делится на 7, значит и число 343 делится на 7.

Число делится на 8 когда 3 его последние цифры – нули, или образуют число которое делится на 8.

Чтобы узнать, делится ли 3-значное число на 8, можно половину единиц прибавить к десяткам. У получившегося числа так же – половину единиц прибавить к десяткам. Если итоговая сумма делится на 2, значит, число делится на 8.

Например, 984:98+2=100=10+0=10 делится на 2, значит и число 984 делится на 8.

Число делится на 9, когда сумма цифр числа делится на 9.

Число делится на 10, когда оно заканчивается 0.

Число делится на 11, когда сумма цифр, с чередующимися знаками делится на 11.

Например, 271436 делится на 11, так как 6 — 3 + 4 — 1 + 7 — 2 = 11 делится на 11.

Число делится на 12, когда оно делится и на 3, и на 4.

Число делится на 13, когда число его десятков, сложенное с учетверённым числом единиц, кратно 13 (например, 845 делится на 13, так как 84+(4*5)=104 делится на 13).

Число делится на 14, когда оно делится и на 2, и на 7.

Число делится на 15, когда оно делится и на 3, и на 5.

Число делится на 17, когда число его десятков, сложенное с увеличенным в 12 раз числом единиц, кратно17.

Например,29053=2905+36=2941=294+12=306=30+72=102=10+24=34. Поскольку, 34 делится на 17, то и 29053 делится на 17.

Признак не всегда удобен, но имеет определённое значение в математике. Есть способ немного проще — число делится на 17, когда разность между число его десятков и упятерённым числом единиц кратна 17.

Число делится на 19, когда число его десятков, сложенное с удвоенным числом единиц, кратно 19.

Например, 646 делится на 19, так как 64+(6*2)=76 делится на 19.

Число делится на 23, когда число его сотен, сложенное с утроенным числом десятков и единиц, кратно 23.

Например, 28842 делится на 23, так как 288+(3*42)=414; продолжаем: 4+(3*14)=46- очевидно, делится на 23.

Число делится на 25, когда число, образованное его последними двумя цифрами делится на 25 (то есть последние две цифры образуют 00,25,50,75).

Разобьём число на группы по 2 цифры справа налево (в самой левой группе может быть одна цифра) и найдём сумму этих групп, считая их двузначными числами. Эта сумма делится на 99 тогда и только тогда, когда само число делится на 99.

Число делится на 100, когда оно заканчивается двумя нулями.

Разобьём число на группы по 2 цифры справа налево (в самой левой группе может быть одна цифра) и найдём сумму этих групп с переменными знаками, считая их двузначными числами. Эта сумма делится на 101, когда само число делится на 101.

Например, 590547 делится на 101, так как 59-05+47=101 делится на 101.

В некотором царстве, в некотором государстве жила принцесса. И однажды ей захотелось узнать ответ на свой вопрос о соседнем королевстве. В соседнем королевстве было 12 фей. За ночь всем феям надо было выполнить одинаковое количество желаний. Всего им надо было выполнить 144 желания. И принцессе захотелось узнать, сколько желаний должна выполнить одна фея за ночь. Но чтобы узнать ответ на вопрос, принцессе надо было слетать в соседнее королевство и спросить у фей. Долететь до королевства принцесса поручила дракону и дала ему на всю дорогу 6 часов. Расстояние до королевства 448,8 км. С какой скоростью должен лететь дракон, чтобы успеть слетать и туда, и обратно?

1) 6:2=3 (часа)- за такое время дракон должен слетать туда или обратно.

2) 448,8:3=149,6 (км/ч)- с такой скоростью должен лететь дракон, что прилететь в своё королевство вовремя.

( Задачу придумала Сторожева Яна).

Дракону надо лететь со скоростью 149,6 км/ч, что прилететь в своё королевство вовремя.

Тем времен дракон прилетел в соседнее королевство. Решение вопроса принцессы оказалось очень простым:

1) 144:12=12(желаний)- должна выполнить 1 фея за ночь.

( Задачу придумала Бордюгова Анастасия).

1 фея должна выполнить 12 желаний за ночь.

Дракон прилетел обратно и получил за ответ на вопрос принцессы вознаграждение: 1,2 кг мороженого. Он решил поделиться мороженым с друзьями. Друзей у него было 7. Сколько мороженого досталось каждому другу и самому дракону?

1) 7+1=8- друзья и сам дракон.

2) 1,2:8=0,15(кг)- досталось каждому другу и самому дракону.

( Задачу придумала Хисемятдинова Нейля).

0,15 кг мороженого досталось каждому другу и самому дракону.

Принцесса решила позвать к себе на работу 7 гномов, чтобы они искали изумруды. И сказала им, что за неделю они должны найти 147 изумрудов. А сама принцесса решила узнать: сколько 7 гномов должны найти изумрудов за 1 день? Сколько 1 гном должен найти изумрудов за 1 день? Сколько 1 гном должен найти изумрудов за неделю?

Вычислить медианную сумму в миллиард чисел — algorithm

Если у вас есть один миллиард номеров и сто компьютеров, каков наилучший способ найти медиану этих чисел?

Одно из решений, которое у меня есть:

  • Разделить набор поровну между компьютерами.
  • Сортировка.
  • Найдите медианы для каждого набора.
  • Сортировка наборов на медианах.
  • Объедините два набора за раз от самой низкой до самой высокой медианной.

Если у нас есть m1 , тогда сначала слияние Set1 и Set2 , и в полученном наборе мы можем отбросить все числа ниже медианы Set12 (объединены). Поэтому в любой момент времени у нас есть равные по размеру множества. Кстати, это невозможно сделать параллельно. Любые идеи?

    2 25
  • 12 окт 2020 2020-10-12 08:15:16
  • anony

25 ответов

Ответ Стива Джессопа неверен:

рассмотрим следующие четыре группы:

Медиана равна 21, которая содержится во второй группе.

Медиана четырех групп равна 6, 24, 30, 36. Общая медиана равна 27.

Итак, после первого цикла четыре группы станут:

21 уже ошибочно отбрасывается.

Этот алгоритм поддерживает только тот случай, когда есть две группы.

  • 12 окт 2020 2020-10-12 08:15:22
  • darklord

Я предлагаю метод для вычисления приблизительно Медиан.:) Если эти миллиарды чисел находятся в случайном порядке, я думаю, что я могу выбрать 1/100 или 1/10 из одного миллиарда чисел в случайном порядке, сортировать их по 100 машин, а затем выбрать медианную из них. Или пусть раскол миллиарда чисел на 100 частей, пусть каждая машина выбирает 1/10 каждой части случайным образом, вычисляет медиану из них. После этого у нас есть 100 номеров, и мы можем рассчитать медиану 100 номера легче. Просто предложение, я не уверен, правильно ли оно исправлено. Но я думаю, что вы можете показать результат не очень хорошему математике.

  • 12 окт 2020 2020-10-12 08:15:22
  • lazyboy

Разделите 1 миллиард чисел на 100 машин. Каждая машина будет иметь 10 ^ 7 номеров.

Для каждого входящего номера на машине сохраните номер на частотной карте, число → счет. Также сохраните минимальное число на каждой машине.

Найти медиану в каждой машине: начиная с минимального числа на каждой машине, суммируйте подсчеты до достижения медианного индекса. Медиана в каждой машине будет составлять ок. меньше и больше 5 * 10 ^ 6 чисел.

Найдите медиану всех медианов, которая будет меньше и больше, чем ок. 50 * 10 ^ 7, что является медианом в 1 миллиард чисел.

Теперь некоторая оптимизация 2-го шага: вместо хранения на частотной карте сохраните подсчеты в массиве переменных бит. Например: скажем, начиная с минимального числа в машине, это число отсчетов частоты:

Вышеуказанные могут быть сохранены в битовом массиве как:

Обратите внимание, что в целом это будет стоить около 10 ^ 7 бит для каждой машины, поскольку каждая машина обрабатывает только 10 ^ 7 номеров. 10 ^ 7bits = 1,25 * 10 ^ 6 байтов, что составляет 1,25 МБ

Таким образом, с помощью вышеупомянутого подхода каждой машине потребуется 1,25 МБ пространства для вычисления локальной медианы. И медиана медианов может быть вычислена из этих 100 локальных медианов, в результате чего медиана составляет 1 миллиард чисел.

  • 12 окт 2020 2020-10-12 08:15:22
  • Shiv

Как насчет этого: — каждый node может принимать 1 миллиард /100 номеров. В каждом node элементы могут быть отсортированы, и медиана может быть найдена. Найдите медиану медиан. мы можем, путем агрегирования подсчетов чисел, меньших медианы медианы на всех узлах, найти x%: y%, расщепленную медианными медианами. Теперь попросите все узлы удалить элементы меньше медианы медианов (с учетом 30%: 70% разделения). 30% номеров удаляются. 70% 1 миллиарда — 700 миллионов. Теперь все узлы, которые удалили менее 3 миллионов узлов, могут отправить эти дополнительные узлы обратно на главный компьютер. Основной компьютер перераспределяется таким образом, что теперь все узлы будут иметь почти равное количество узлов (7 миллионов). Теперь, когда проблема сводится к 700 миллионам чисел. продолжается, пока у нас не будет меньшего набора, который может быть вычислен на одном компьютере.

  • 12 окт 2020 2020-10-12 08:15:21
  • anony

Моя копейка стоит, после всего, что уже было воспитано другими:

Отправка N номеров на 100 машин также O (N). Таким образом, чтобы сделать использование 100 машин интересным, либо связь должна быть относительно быстрой, либо N настолько велика, что одна машина не может ее обрабатывать, а N/100 выполнимо, или мы просто хотим рассмотреть математическую проблему, не беспокоясь о ней datacommunication.

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

Рассмотрим следующий подход, когда одной машине присваивается «мастер» для некоторой общей обработки. Это будет сравнительно быстро, поэтому «мастер» также участвует в общих задачах, которые выполняет каждая машина.

  • Каждая машина получает N/100 номеров, вычисляет свою собственную медиану и отправляет эту информацию мастеру.
  • Мастер компилирует отсортированный список всех отдельных медианов и отправляет их обратно на каждую машину, определяя упорядоченную последовательность ведер (на каждой машине то же самое), по одному для каждого медианного значения (одно значение ведра), а один для каждый интервал между соседними медианами. Конечно, есть также нижние и верхние ковши для значений ниже самой низкой медианной и выше самой высокой.
  • Каждая машина вычисляет, сколько чисел попадает в каждое ведро и передает эту информацию обратно ведущему устройству.
  • Мастер определяет, в каком ведре содержится медиана, сколько нижних значений (в целом) опускается ниже этого ведра и сколько выше.
  • Если выбранное ведро представляет собой единичное ведро (одно из медианов), оливковое выделенное ведро содержит только 1 (N нечетных) или 2 (N четных) значений, которые мы выполнили. В противном случае мы повторим описанные выше шаги со следующими (очевидными) изменениями:
  • Только числа из выбранного ковша (re) распределяются от ведущего к 100 машинам и, кроме того,
  • Мы не собираемся вычислять (на каждой машине) медианное, а k-е значение, где мы учитываем, сколько более высоких чисел было отброшено от общего числа и сколько меньших чисел. Концептуально каждая машина имеет свою долю отброшенных низких/больших чисел и учитывает это при вычислении новой медианы в наборе, которая (концептуально) включает (свою долю) отброшенные числа.
  • Маленькое мышление убедит вас в том, что на каждом шаге общее количество анализируемых значений сокращается в два раза (в два раза больший случай, вы можете ожидать значительно лучшего снижения). Отсюда получаем:
  • Предполагая, что поиск медианного (или k-го значения), который является O (N), принимает время c * N, где префактор c не слишком сильно изменяется с N, так что мы можем считать его константой для момент, мы получим наш окончательный результат не более 2 * c * N/100 раз. Поэтому использование 100 машин дает нам коэффициент ускорения 100/2 (не менее).
  • Как отмечалось ранее: время, связанное с передачей чисел между машинами, может сделать его более привлекательным, чтобы просто делать все на одной машине. Однако, ЕСЛИ мы идем для распределенного подхода, общее количество чисел, которые будут переданы во всех шагах вместе, не будет превышать 2 * N (N в первый раз, 12 окт 2020 2020-10-12 08:15:21
  • Bert te Velde

Я бы сделал это следующим образом:

в начале все 100 работают, чтобы найти самое высокое и самое низкое число; каждый компьютер имеет свою часть базы данных/файла, который он запрашивает;

когда найдены самые высокие и самые низкие числа, один компьютер считывает данные и равномерно распределяет каждое число до остальной части 99; числа распределяются равными интервалами; (можно взять от -100 миллионов до 0, другое — от 0 до 100 миллионов и т.д.);

При получении номеров каждый из 99 компьютеров уже сортирует их,

Затем легко найти медиану. Посмотрите, сколько чисел имеет каждый компьютер, добавьте все (сумма количества чисел, а не самих чисел), разделите на 2; вычислить, в каком компьютере это число, и при каком индексе

P.S. Кажется, здесь много путаницы; MEDIAN — это ЧИСЛО В СРЕДЕ СОРТИРОВАННОГО СПИСКА НОМЕРОВ!

  • 12 окт 2020 2020-10-12 08:15:21
  • Ion

Вы можете использовать метод дерева турниров для поиска медианы. Мы можем создать дерево с 1000 остальными узлами, чтобы каждый лист node представлял собой массив. Затем мы проводим n/2 турниров между различными массивами. Результатом является значение для корня после n/2 турниров.

  • 12 окт 2020 2020-10-12 08:15:21
  • karan kapoor

Хорошо, предположим, что вы знаете, что количество различных целых чисел (скажем) 4 миллиарда, тогда вы можете загрузить их в 64-килобайтные ведра и получить распределенное количество для каждого ведра с каждой машины в кластере (100 компьютеров). Объедините все эти подсчеты. Теперь найдите ведро, которое имеет медиану, и на этот раз запросите только ведра для элементов 64k, которые будут находиться в вашем целевом ковше. Для этого требуется O (1) (в частности, 2) запроса по вашему «кластеру».: D

  • 12 окт 2020 2020-10-12 08:15:21
  • gandharv garg

Сначала рассмотрим, как найти медиану n чисел на одной машине: Я в основном использую стратегию разделения.

Проблема: выбор (n, n/2): Найдите n/2-е число из наименьшего числа.

Вы выбираете средний элемент k и разделяете данные на 2 подматрицы. первый содержит все элементы = k.

if sizeof (1st sub-array) >= n/2, вы знаете, что этот вспомогательный массив содержит медиану. Затем вы можете сбросить вторую подматрицу. Решите эту проблему выбор (sizeof 1st sub-array, n/2).

В противном случае сбросьте этот 1-й подмашину и решите выбор (2-й подмассив, n/2 — sizeof (1-й подмассива))

Сделайте это рекурсивно.

временная сложность O (n) ожидаемое время.

Теперь, если у нас много машин, на каждой итерации нам нужно обработать массив для разделения, мы распределяем массив на разные машины. Каждая машина обрабатывает свой кусок массива, а отправляет сводку на управляющую машину концентратора, т.е. Размер 1-го подмассива и размер второго подмассива. Машины-концентраторы суммируют сводки и определяют, какой подмассив (1-й или 2-й) продолжить процесс и второй параметр выбора и отправить его на каждую машину. и т.д.

Этот алгоритм может быть реализован очень аккуратно, используя сокращение карты?

Как это выглядит?

  • 12 окт 2020 2020-10-12 08:15:20
  • xyz

Если числа не различны и принадлежат только определенному диапазону, то есть они повторяются, то простое решение, которое приходит мне на ум, состоит в том, чтобы распределить числа среди 99 машин одинаково и сохранить одну машину в качестве мастера, Теперь каждая машина выполняет итерацию по заданным числам и сохраняет количество каждого числа в хэш-наборе. Каждый раз, когда число повторяется в наборе чисел, выделенных этому конкретному компьютеру, он обновляет свой счет в хэш-наборе.

Затем все машины возвращают свой хэш на мастер-машину. Мастер-машина объединяет хэш-множества, суммируя подсчет того же ключа, найденного в хэш-наборе. Например, хэш-набор машин # 1 имел запись ( «1» , 7), а в хэш-наборе машины №2 была запись ( «1» , 9), поэтому мастер-машина при расчете наборов хэшей делает запись ( «1» , 16) и т.д.

После того, как хэш-множества были объединены, просто отберите ключи, и теперь вы можете легко найти (n/2) -й элемент и (n + 2/2) -й элемент из отсортированного набора хэшей.

Этот метод не будет полезен, если количество миллиардов будет различным.

  • 12 окт 2020 2020-10-12 08:15:20
  • Eric B.

Более простой способ — иметь взвешенные числа.

  • Разделить большой набор компьютеров.
  • Сортировка каждого набора
  • итерация через малый набор и вычисление весов для повторяющихся элементов
  • объединить каждый 2 набора в 1 (каждый уже отсортирован) обновление весов
  • сохраняйте слияние наборов, пока не получите только один набор
  • итерируйте этот набор, накапливая весы, пока не достигнете OneBillion/2
  • 12 окт 2020 2020-10-12 08:15:20
  • Ziad Nasser

Это можно сделать на узлах с использованием данных, которые не сортируются по узлам (скажем, из файлов журналов) следующим образом.

Существует 1 родительский node и 99 дочерних узлов. У дочерних узлов есть два вызова api:

  • stats(): возвращает min, max и count
  • compare (median_guess): возвращает количество совпадений, количество меньше значения и число больше значения

Родительский node вызывает stats() для всех дочерних узлов, отмечая минимум и максимум всех узлов.

Теперь двоичный поиск может быть выполнен следующим образом:

  • Поменяйте минимальное и максимальное округление — это медианная «догадка»
  • Если значение больше, чем счет больше, чем меньше, установите минимальное значение в предположение
  • Если значение больше, чем счет меньше, чем счетчик, установите максимальное значение в предположении
  • Если счетчик является нечетным, когда минимальные и максимальные значения равны
  • Если счетчик даже заканчивается, когда максимум 12 окт 2020 2020-10-12 08:15:20
  • teambob

Разделите номера 10 ^ 9, 10 ^ 7 на каждый компьютер

80 МБ на каждом. Каждый компьютер сортирует свои номера. Затем компьютер 1 объединяет свои собственные номера с номерами компьютеров 2, компьютеров 3 и 4 и т.д. Затем компьютер 1 записывает половину номеров обратно в 2, 3 и 4 и т.д. Затем 1 слияние сортирует числа с компьютеров 1,2,3,4, записывает их обратно. И так далее. В зависимости от размера ОЗУ на компьютерах вы можете уйти, не записывая все номера на отдельные компьютеры на каждом шаге, вы можете накапливать числа на компьютере 1 для нескольких шагов, но вы делаете математику.

О, наконец, получим среднее значение 500000000 и 500000001st (но проверьте, что там достаточно 00s, я этого не делал).

EDIT: @Roman — хорошо, если вы не можете поверить в это, даже если это правда, тогда нет смысла в том, чтобы я раскрывал правду или ложь предложения. Я хотел сказать, что грубая сила иногда бьется умнее в гонке. Мне потребовалось около 15 секунд, чтобы разработать алгоритм, который я уверен, что я могу реализовать, который будет работать, и который будет адаптирован к широкому диапазону размеров входов и номеров компьютеров и настраивается на характеристики компьютеров и сетей. Если вам понадобится, или кто-либо еще, скажите 15 минут, чтобы разработать более сложный алгоритм, у меня есть преимущество 14m45s, чтобы закодировать мое решение и запустить его.

Но я свободно признаю, что это все утверждение, я ничего не измерил.

  • 12 окт 2020 2020-10-12 08:15:20
  • High Performance Mark

Это можно сделать быстрее, чем алгоритм, проголосовавший (n log n)

— Алгоритм распределения распределенных выборок статистики — O (n)
Упростите проблему исходной проблеме нахождения k-го числа в несортированном массиве.
— подсчет гистограммы сортировки O (n)
Вы должны принять некоторые свойства о диапазоне чисел — может ли диапазон соответствовать памяти? — Сортировка внешнего слияния — O (n log n) — описано выше
Вы в основном сортируете числа на первом проходе, затем находите медианную на втором.
— Если что-то известно о распределении чисел другим алгоритмы могут быть созданы.

  • 12 окт 2020 2020-10-12 08:15:19
  • user1712376

Я думаю, что ответ Стива Джессопа будет самым быстрым.

Если сетевой перенос размер является узким местом, вот еще один подход.

  • 12 окт 2020 2020-10-12 08:15:19
  • Cem

Это зависит от ваших данных. В худшем случае это равномерно распределенные числа.

В этом случае вы можете найти медиану в O (N) времени, как в этом примере:

Предположим, что ваши цифры составляют 2,7,5,10,1,6,4,4,6,10,4,7,1,8,4,9,9,3,4,3 (диапазон составляет 1 -10).

Создаем 3 ведра: 1-3, 4-7, 8-10. Обратите внимание, что верх и низ имеют одинаковый размер.

Мы заполняем ведра числами, подсчитываем, сколько падений в каждом, максимальное и мин

  • низкий (5): 2,1,1,3,3, min 1, max 3
  • средний (10): 7,5,6,4,4,6,4,7,4,4, min 4, max 7
  • high (5): 10, 10, 8, 9, 9, min 8, max 10

Среднее значение попадает в среднее ведро, мы пренебрегаем остальными

Создаем 3 ведра: 4, 5-6, 7. Низкий начнется со счета 5 и с максимумом 3 и выше с минусом 8 и количеством 5.

Для каждого числа мы подсчитываем, сколько падений в низком и высоком ведрах, max и min, и сохраняем среднее ведро.

  • старый низкий (5)
  • low (5): 4, 4, 4, 4, 4, max 4
  • средний (3): 5,6,6
  • высокий (2): 7, 7, мин. 7
  • старый высокий (5)

Теперь мы можем непосредственно вычислить медиану: у нас есть такая ситуация

так что медиана равна 4.5.

Предполагая, что вы немного знаете о распределении, вы можете точно настроить, как определить диапазоны для оптимизации скорости. В любом случае производительность должна идти с O (N), потому что 1 + 1/3 + 1/9. = 1,5

Вам нужны мин и макс из-за краевых случаев (например, если медиана является средним значением между максимумом старого минимума и следующим элементом).

Все эти операции могут быть распараллелены, вы можете дать 1/100 данных на каждый компьютер и вычислить 3 ведра в каждом node, а затем распределить ведро, которое вы храните. Это снова заставляет вас эффективно использовать сеть, потому что каждый номер передается в среднем в 1,5 раза (так что O (N)). Вы можете даже побить это, если вы пропускаете только минимальные числа среди узлов (например, если node 1 имеет 100 номеров, а node 2 имеет 150 номеров, то node 2 может дать 25 номеров node 1).

Если вы не знаете больше о дистрибутиве, я сомневаюсь, что вы можете сделать лучше, чем O (N) здесь, потому что вам действительно нужно считать элементы хотя бы один раз.

  • 12 окт 2020 2020-10-12 08:15:19
  • Sklivvz

Один компьютер более чем достаточно, чтобы решить проблему.

Но допустим, что есть 100 компьютеров. Единственное сложное, что вам нужно сделать — это отсортировать список. Разделите его на 100 частей, отправьте одну часть на каждый компьютер, позвольте им сортироваться там и объедините части после этого.

Затем возьмем номер из середины отсортированного списка (т.е. с индексом 5 000 000 000).

  • 12 окт 2020 2020-10-12 08:15:19
  • Roman

Это может удивить людей, но если числа целые, достаточно маленькие, чтобы вписаться в 32-битные (или меньше) — просто сделайте сортировку в виде ведра! Требуется только 16 ГБ оперативной памяти для любого количества 32-битных int и работает в O (n), что должно превосходить любые распределенные системы для разумного n, например. миллиард.

Как только у вас есть отсортированный список, тривиально выбрать медиану. Фактически, вам не нужно создавать отсортированный список, но только смотреть на ведра должны делать это.

Мастер Йода рекомендует:  Обзор сервиса продажи вечных ссылок PR.Sape

Простая реализация показана ниже. Работает только для 16-битных целых чисел, но расширение до 32-битного должно быть простым.

Использование текстового файла с миллиардом (10 9 ) чисел и работающим с time таким образом

дает время работы на моей машине 1m49.293s. Большая часть времени работы, вероятно, также является диском IO.

  • 12 окт 2020 2020-10-12 08:15:19
  • vidstige

Как ни странно, я думаю, что если у вас достаточно компьютеров, вам лучше сортировать, чем использовать алгоритмы медианного поиска O(n) . (Если ваши ядра не очень, очень медленные, я бы просто использовал один и использовал алгоритм поиска O(n) для всего лишь 1е9 чисел, если бы у вас было 1e12, это могло бы быть менее практичным.)

В любом случае, допустим, для решения этой проблемы у нас больше, чем log n ядер, и мы не заботимся об энергопотреблении, просто получив ответ быстро. Предположим далее, что это SMP-машина со всеми данными, уже загруженными в память. (Например, 32-ядерные машины Sun относятся к этому типу.)

Один поток прерывает список вслепую в части равного размера и сообщает другим потокам M сортировать их. Эти течения старательно делают это в (n/M) log (n/M) времени. Затем они возвращают не только их медианы, но, скажем, их 25-й и 75-й процентили (извращенные худшие случаи лучше, если вы выбираете несколько разные цифры). Теперь у вас есть 4M диапазона данных. Затем вы сортируете эти диапазоны и работаете вверх по списку до тех пор, пока не найдете число, чтобы, если вы выбрасываете каждый диапазон, который меньше или содержит номер, вы будете выбросить половину своих данных. Это ваша нижняя граница для медианы. Сделайте то же самое для верхней границы. Это требует времени M log M , и все ядра должны ждать его, поэтому он действительно растрачивает потенциальное время M^2 log M . Теперь у вас есть один поток, который расскажет другим о том, чтобы выбросить все данные за пределы диапазона (вы должны выбросить около половины на каждый проход) и повторить — это тривиально быстрая операция, так как данные уже отсортированы. Вам не нужно повторять это больше, чем log(n/M) раз, прежде чем быстрее, просто возьмите оставшиеся данные и используйте на нем стандартный медианный искатель O(n) .

Итак, общая сложность — это что-то вроде O((n/M) log (n/M) + M^2 log M log (n/M)) . Таким образом, это быстрее, чем O(n) медианная сортировка на одном ядре, если M >> log(n/M) и M^3 log M , что верно для описанного вами сценария.

Я думаю, что это действительно плохая идея, учитывая, насколько она неэффективна, но она быстрее.

  • 12 окт 2020 2020-10-12 08:15:18
  • Rex Kerr

Медиана для этого набора чисел

2, 3, 5, 7, 11, 13, 67, 71, 73, 79, 83, 89, 97

Медиана для этого набора чисел

2, 3, 5, 7, 11, 13, 67, 71, 73, 79, 83, 89

Предполагая, что вопрос был около 1 000 000 000 целых чисел (x), где 0 >= x 12 окт 2020 2020-10-12 08:15:18

  • dbasnett
  • Один миллиард на самом деле довольно скучная задача для современного компьютера. Мы говорим о 4 байтах четырех байтовых целых чисел. 4 ГБ. что ОЗУ некоторых смартфонов.

    Выход на моей машине:

    Таким образом, это завершается на моей машине менее чем за две минуты (1:43 из которых 0:10 — генерировать случайные числа), используя одно ядро, и даже делает полный сортировку. Ничего особенного.

    Это, безусловно, интересная задача для больших наборов чисел. Я просто хочу сказать здесь: один миллиард — это арахис. Поэтому подумайте дважды, прежде чем начинать бросать сложные решения на удивительно простые задачи;)

    • 12 окт 2020 2020-10-12 08:15:18
    • sfussenegger

    Оценка статистики порядка, как медиана и 99-й процентили, может быть эффективно распределена с помощью таких алгоритмов, как t-digest или Q- дайджест.

    Используя любой алгоритм, каждый node создает дайджест, представляющий распределение значений, хранящихся локально. Сборники собираются с помощью единственного node, объединенного (эффективно суммируя распределения), а затем медиана или любой другой процентиль могут быть просмотрены.

    Этот подход используется elasticsearch и, предположительно, BigQuery (собирается описание функции QUANTILES).

    • 12 окт 2020 2020-10-12 08:15:18
    • Richard Poole

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

    1) Выберите 1000 значений случайным образом из миллиарда и используйте их, чтобы получить представление о распределении чисел, особенно в диапазоне.

    2) Вместо того, чтобы сортировать значения, выделите их в ведра на основе только что вычисленного распределения. Количество ведер выбрано так, чтобы компьютер мог эффективно их обрабатывать, но в противном случае он был бы таким же большим, как удобно. Диапазоны ковша должны быть равны примерно одинаковому количеству значений в каждом ковше (это не имеет решающего значения для алгоритма, но это помогает эффективности. Может потребоваться 100 000 ковшей). Обратите внимание на количество значений в каждом ковше. Это процесс O (n).

    3) Узнайте, в каком ковше находится медиана. Это можно сделать, просто проанализировав общие числа в каждом ковше.

    4) Найдите фактическую медиану, исследуя значения в этом ковше. Вы можете использовать этот вид здесь, если хотите, поскольку вы сортируете только 10 000 номеров. Если количество значений в этом ведре велико, вы можете снова использовать этот алгоритм, пока у вас не будет достаточно небольшого числа для сортировки.

    Этот подход тривиально распараллеливается путем деления значений между компьютерами. Каждый компьютер сообщает итоговые значения в каждом ведре на компьютер «управления», который выполняет шаг 3. Для шага 4 каждый компьютер отправляет (отсортированные) значения в соответствующем ведре на управляющий компьютер (вы можете параллельно выполнять оба этих алгоритма, но это, вероятно, не стоит).

    Общий процесс — O (n), так как обе стадии 3 и 4 тривиальны, если количество ведер достаточно велико.

    • 12 окт 2020 2020-10-12 08:15:18
    • DJClayworth

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

    Машина 1 должна называться «управляющей машиной», и ради аргумента либо она начинается со всех данных, и отправляет ее в равных посылках другим 99 машинам, либо данные равномерно распределяются между машинами, и он отправляет 1/99 своих данных каждому из других. Разделы не обязательно должны быть равными, просто закрывающимися.

    Каждая другая машина сортирует свои данные и делает это таким образом, чтобы сначала находить более низкие значения. Так, например, quicksort, всегда сначала сортируя нижнюю часть раздела [*]. Он записывает свои данные обратно на управляющую машину в порядке возрастания, как только может (используя асинхронный ввод-вывод, чтобы продолжить сортировку, и, вероятно, с помощью Nagle on: немного экспериментируйте).

    Управляющая машина выполняет 99-точечное слияние данных по мере поступления, но отбрасывает объединенные данные, просто сохраняя подсчет количества значений, которые он видел. Он вычисляет медиану как среднее значение значений в 1/2 миллиарда и 1/2 миллиарда плюс.

    Это страдает от проблемы «самого медленного в стаде». Алгоритм не может быть завершен до тех пор, пока каждое значение, меньшее, чем медиана, не будет отправлено с помощью сортирующей машины. Существует разумная вероятность того, что одно из таких значений будет достаточно высоким в пределах своей части данных. Таким образом, как только начальное разбиение данных будет завершено, оценочное время работы представляет собой комбинацию времени для сортировки 1/99-й данных и отправки ее обратно на управляющий компьютер, а время для элемента управления читать 1/2 данных, «Комбинация» находится где-то между максимумом и суммой тех времен, вероятно, близким к максимальному.

    Мой инстинкт заключается в том, что для передачи данных по сети быстрее, чем сортировка (не говоря уже о выборе медианы), она должна быть довольно чертовой быстрой сетью. Возможно, будет лучшая перспектива, если сеть может считаться мгновенной, например, если у вас есть 100 ядер с равным доступом к ОЗУ, содержащим данные.

    Так как сетевой ввод-вывод, скорее всего, является границей, могут быть некоторые трюки, которые вы можете играть, по крайней мере, для данных, возвращающихся на управляющую машину. Например, вместо отправки «1,2,3. 100», возможно, сортировочная машина может отправить сообщение, означающее «100 значений меньше 101». Затем контрольная машина могла выполнить модифицированное слияние, в котором она найдет наименьшее из всех значений верхнего уровня, а затем сообщит всем сортировочным машинам, что это такое, чтобы они (а) могли сказать машине управления, как многие значения «подсчитываются» ниже этого значения и (б) возобновляют отправку своих отсортированных данных с этой точки.

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

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

    [*] доступный доступ к стеклу — ваш выбор того, какая часть должна выполняться первой, ограничена, если у вас нет O (N) дополнительного пространства. Но если у вас достаточно свободного места, вы можете взять свой выбор, и если у вас недостаточно места, вы можете хотя бы использовать то, что вам нужно, чтобы сократить некоторые углы, сделав небольшую часть сначала для первых нескольких разделов.

    Напишите программу, чтобы найти 100 самых больших чисел из массива в 1 миллиард чисел

    недавно я присутствовал на интервью, где меня попросили » написать программу, чтобы найти 100 самых больших чисел из массива 1 миллиарда чисел.»

    я смог дать только решение грубой силы, которое должно было отсортировать массив в o(nlogn) временной сложности и взять последние 100 чисел.

    интервьюер искал лучшую сложность времени, я попробовал несколько других решений, но не смог ответить ему. Есть ли лучшее решение сложности времени?

    30 ответов

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

    EDIT: как отметил Dev, с приоритетной очередью, реализованной с кучей, сложность вставки в очередь составляет O(logN)

    в худшем случае вы получите billionlog2(100) что лучше чем billionlog2(billion)

    в общем случае, если вам нужны самые большие K чисел из множества N чисел, сложность O(NlogK) , а не O(NlogN) , это может быть очень значительным, когда K очень мало по сравнению с N.

    EDIT2:

    ожидаемое время этого алгоритма довольно интересно, так как на каждой итерации вставка может произойти или не произойти. Вероятность того, что i-е число будет вставлено в очередь, равна вероятности случайного переменная больше, чем по крайней мере i-K случайные величины из того же распределения (первые k чисел автоматически добавляются в очередь). Мы можем использовать статистику заказов (см. ссылке) для вычисления этой вероятности. Например, предположим, что числа были выбраны случайным образом равномерно из <0, 1>, ожидаемое значение (i-K) — го числа (из i чисел) — (i-k)/i , и вероятность случайной величины больше, чем это значение 1-[(i-k)/i] = k/i .

    таким образом, ожидаемое количество вставок:

    и ожидаемое время выполнения можно выразить как:

    ( k время для создания очереди с первым k элементы, затем n-k сравнения и ожидаемое количество вставок, как описано выше, каждый принимает среднее значение log(k)/2 времени)

    обратите внимание, что при N очень большой по сравнению с K , это выражение намного ближе к n , а не NlogK . Это несколько интуитивно, так как в случае вопроса даже после 10000 итераций (что очень мало по сравнению с миллиардом) вероятность того, что число будет вставлено в очередь, очень мала.

    Если это задано в интервью, я думаю, что интервьюер, вероятно, хочет увидеть ваш процесс решения проблем, а не только ваши знания алгоритмов.

    описание довольно общее, поэтому, возможно, вы можете спросить его диапазон или значение этих чисел, чтобы прояснить проблему. Это может произвести впечатление на интервьюера. Если, например,эти цифры означают возраст людей внутри страны (например, Китай), то это гораздо более простая проблема. С разумным предположением, что никто из живущих не старше 200 лет, вы можете использовать массив int размером 200(возможно, 201), чтобы подсчитать количество людей с тем же возрастом всего за одну итерацию. Здесь индекс означает возраст. После этого это кусок пирога, чтобы найти 100 наибольшее количество. Кстати, этот Альго называется подсчет вроде.

    в любом случае, сделать вопрос более конкретным и ясным хорошо для вас в интервью.

    вы можете перебирать числа, которые принимают O (n)

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

    мин этой круговой очереди — это ваше новое значение сравнения. Продолжайте добавлять в эту очередь. Если заполнено, извлеките минимум из очереди.

    Я понял, что это помечено «алгоритмом», но выбросит некоторые другие варианты, так как он, вероятно, также должен быть помечен «интервью».

    каков источник 1 миллиарда чисел? Если это база данных, то «выбрать значение из порядка таблицы по значению desc limit 100» будет делать эту работу довольно хорошо — могут быть диалектные различия.

    Это одноразово, или что-то, что будет повторяться? Если повторять, то как часто? Если это одноразовый и данные в файле «cat srcfile / sort (options as needed) | head -100» заставит вас быстро выполнять продуктивную работу, за которую вам платят, пока компьютер обрабатывает эту тривиальную работу.

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

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

    удачи на следующем интервью.

    можно использовать быстрый выбор алгоритма чтобы найти число в индексе (по порядку) [billion-101] а затем повторите числа и найдите числа, которые больше от этого числа.

    это время алгоритма: 2 X O(N) = O (N) (средняя производительность случая)

    второй вариант, как Томас Джангблат предлагаем:

    использовать кучу построение максимальной кучи займет O(N),тогда верхние 100 максимальных чисел будут в верхней части кучи, все, что вам нужно, это вытащить их из кучи(100 X O(Log (N)).

    это время алгоритма: O(N) + 100 X O(Log(N)) = O (N)

    моей непосредственной реакцией на это было бы использование кучи, но есть способ использовать QuickSelect без сохранения всех входных значений под рукой в любой момент.

    создайте массив размером 200 и заполните его первыми 200 входными значениями. Запустите QuickSelect и отбросьте низкие 100, оставив вам 100 свободных мест. Прочитайте следующие 100 входных значений и снова запустите QuickSelect. Продолжайте, пока вы не запустите хоть весь вход в пакетах 100.

    В конце вы есть 100 значений. Для n значений вы выполнили QuickSelect примерно N / 100 раз. Каждый Quickselect стоит около 200 раз некоторую константу, поэтому общая стоимость 2n раз некоторую константу. Это выглядит линейным по размеру ввода для меня, независимо от размера параметра, который я жестко привязываю к 100 в этом объяснении.

    хотя другое решение quickselect было понижено, факт остается фактом, что quickselect найдет решение быстрее, чем с помощью очереди размером 100. Quickselect имеет ожидаемое время работы 2n + o (n) с точки зрения сравнения. Очень просто реализация будет

    Это займет 3n + o(n) сравнения в среднем. Кроме того, его можно сделать более эффективным, используя тот факт, что quickselect оставит самые большие 100 элементов в массиве в 100 справа-большинство локаций. Таким образом, время работы может быть улучшено до 2n+o (n).

    существует проблема, что это ожидаемое время работы, и не в худшем случае, но с помощью достойной стратегии выбора поворота (например, выберите 21 элемент случайным образом и выберите медиану из этих 21 в качестве поворота), тогда количество сравнений может быть гарантировано с высокой вероятностью не более (2+c)n для сколь угодно малой константы c.

    фактически, используя оптимизированную стратегию выборки (например, выборка sqrt (n) элементов случайным образом и выберите 99-й процентиль), время работы может быть сведено к (1+c) n + o(n) для сколь угодно малого c (предполагая, что K, количество элементов, которые будут выбраны o(n)).

    С другой стороны, использование очереди размером 100 потребует o(log(100)n) сравнения, а база журнала 2 из 100 примерно равна 6.6.

    Если мы рассмотрим эту проблему в более абстрактном смысле выбора наибольших k элементов из массива размер N, где K=o(N), но оба K и N идут в бесконечность, тогда время работы версии quickselect будет O(N), а версия очереди будет O (N log K), поэтому в этом смысле quickselect также асимптотически превосходит.

    в комментариях было упомянуто, что решение очереди будет выполняться в ожидаемое время n + K log N на случайном входе. Конечно, предположение о случайном вводе никогда не будет действительным, если вопрос не сформулирует его явно. Решение очередей можно пройти массив в случайном порядке, но это потребует дополнительных затрат на N вызовов генератора случайных чисел, а также либо перестановки всего входного массива, либо выделения нового массива длины N, содержащего случайные индексы.

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

    возьмите первые 100 чисел миллиарда и отсортируйте их. теперь просто повторите миллиард, если исходное число выше наименьшего из 100, вставьте в порядке сортировки. То, что вы в конечном итоге с чем-то гораздо ближе к O(n) по размеру набора.

    (1) куча (priorityQueue)

    поддерживать минимальную кучу размером 100. Пересечь массив. Как только элемент меньше первого элемента в куче, замените его.

    (2) Map-уменьшить модель.

    Это очень похоже на пример подсчета слов в hadoop. Задание карты: подсчитайте частоту или время появления каждого элемента. Уменьшить: получить верхний элемент K.

    обычно я бы дал рекрутеру два ответа. Дать им все, что они захотят. Конечно, map reduce coding будет трудоемким, потому что вы должны знать все точные параметры. Не повредит попрактиковаться. удача.

    очень простым решением было бы перебирать массив 100 раз. Которая составляет O(n) .

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

    вдохновленный ответом @ron teller, вот программа barebones C, чтобы делать то, что вы хотите.

    на моей машине (core i3 с быстрым SSD) требуется 25 секунд и 1724 сортировки. Я создал двоичный файл с dd if=/dev/urandom/ count=1000000000 bs=1 для этого работать.

    очевидно, что есть проблемы с производительностью при чтении только 4 байта за раз — с диска, но это для примера. С другой стороны, очень мало памяти.

    самым простым решением является сканирование большого массива миллиардов чисел и удержание 100 самых больших значений, найденных до сих пор в буфере малого массива без сортировки, и запоминание наименьшего значения этого буфера. Сначала я думал, что этот метод был предложен fordprefect, но в комментарии он сказал, что он предположил, что структура данных 100 номеров реализуется как куча. Всякий раз, когда найдено новое число, которое больше, то минимум в буфере перезаписывается новым найденным значением и буфером снова выполняется поиск текущего минимума. Если числа в массиве миллиардов чисел распределены случайным образом большую часть времени, значение из большого массива сравнивается с минимумом малого массива и отбрасывается. Только для очень очень малой части числа значение должно быть вставлено в небольшой массив. Таким образом, разницей в манипулировании структурой данных, содержащей небольшие числа, можно пренебречь. Для небольшого количества элементов трудно определить, используется ли приоритетная очередь на самом деле быстрее, чем использовать мой наивный подход.

    Я хочу оценить количество вставок в небольшом буфере массива 100 элементов при сканировании массива 10^9 элементов. Программа сканирует первые 1000 элементов этого большого массива и должна вставить не более 1000 элементов в буфер. Буфер содержит 100 элементов из 1000 отсканированных элементов, то есть 0,1 отсканированного элемента. Поэтому мы предполагаем, что вероятность того, что значение из большого массива больше текущего минимальная буфера составляет около 0,1 такой элемент должен быть вставлен в буфер . Теперь программа сканирует следующие 10^4 элемента из большого массива. Потому что минимум буфера будет увеличиваться каждый раз, когда вставляется новый элемент. Мы оценили, что отношение элементов больше нашего текущего минимума составляет около 0,1, и поэтому есть 0,1*10^4=1000 элементов для вставки. На самом деле ожидаемое количество элементов, вставленных в буфер, будет меньше. После сканирования этого 10^4 элементы доля чисел в буфере будет около 0,01 от элементов, отсканированных до сих пор. Поэтому при сканировании следующих 10^5 чисел мы предполагаем, что в буфер будет вставлено не более 0.01*10^5=1000. Продолжая эту аргументацию мы вставили около 7000 значения после сканирования 1000+10^4+10^5+. +10^9

    10^9 элементов большого массива. Поэтому при сканировании массива с 10^9 элементами случайного размера мы ожидаем не более 10^4 (=7000 округленных) вставок в буфер. После каждого вставка в буфер должен быть найден новый минимум. Если буфер представляет собой простой массив, нам нужно 100 сравнение, чтобы найти новый минимум. Если буфер является другой структурой данных (например, кучей), нам нужно по крайней мере 1 сравнение, чтобы найти минимум. Для сравнения элементов большого массива нам нужно 10^9 сравнений. В общем, все, что нам нужно. 10^9+100*10^4=1.001 * 10^9 сравнения при использовании массива в качестве буфера и по крайней мере 1.000 * 10^9 сравнения при использовании другого типа структуры данных (например, куча.) Таким образом, использование кучи приносит только выигрыш в 0,1%, если производительность определяется количеством сравнения. Но какова разница во времени выполнения между вставкой элемента в кучу 100 элементов и заменой элемента в массиве 100 элементов и поиском его нового минимума?

    на теоретическом уровне: сколько сравнений требуется для вставки в кучу. Я знаю, что это O(log (n)), но насколько велик постоянный фактор? Я

    на машинном уровне: каково влияние кэширования и прогнозирования ветвей на время выполнения вставки кучи и линейного поиска в массиве.

    на уровне реализации: какие дополнительные затраты скрыты в структуре данных кучи, предоставляемой библиотекой или компилятором?

    Я думаю, что это некоторые из вопросов, на которые нужно ответить, прежде чем пытаться оценить реальную разницу между производительность кучи 100 элементов или массива 100 элементов. Поэтому имеет смысл провести эксперимент и измерить реальную производительность.

    алгоритм наибольших X элементов из n:

    Я вызову возвращаемое значение список. Это набор из-х элементов (на мой взгляд это должен быть связный список)

    • первые элементы x берутся из пула «как они приходят» и сортируются в списке( это делается в постоянное время, так как x рассматривается как constant — O(X log (x) ) time)
    • для каждого элемента, который идет дальше, мы проверяем, если он больше, чем наименьший элемент в списке, и если мы выскакиваем наименьший и вставляем текущий элемент в список. Поскольку это упорядоченный список, каждый элемент должен найти свое место в логарифмическом времени (двоичный поиск), и поскольку он упорядочен, вставка списка не является проблемой. Каждый шаг также выполняется в постоянное время (o (log (x))).

    Итак, каков наихудший сценарий?

    X log (x) + (n-x) (log(x)+1) = nlog(x) + n — x

    Так что это O(n) время для худшего случая. +1 — это проверка, если число больше, чем наименьшее в списке. Предполагаемое время для среднего случая будет зависеть от математического распределения этих n элементов.

    возможных улучшениях

    этот алгоритм может быть немного улучшен для наихудшего сценария, но IMHO (я не могу доказать это утверждение), что ухудшит среднее поведение. Асимптотическое поведение будет таким же.

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

    X log (x) + (n-x)log(x) = nlog (x)

    для этого примера я не вижу никаких дальнейших улучшений. Но вы должны спросить себя — что, если мне придется делать это больше, чем log(n) раз и для разных x-es? Очевидно, мы бы отсортируйте этот массив в O (N log (n)) и возьмите наш элемент x всякий раз, когда они нам нужны.

    на этот вопрос будет дан ответ с N log (100) complexity (вместо N log N) только с одной строкой кода C++.

    окончательный ответ будет вектором, где первые 100 элементов гарантированно будут 100 самыми большими числами вашего массива, а остальные элементы неупорядочены

    C++ STL (стандартная библиотека) довольно удобна для такого рода проблем.

    примечание: Я не говорю, что это оптимальное решение, но он спас бы ваше интервью.

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

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

    Итак, мы выбираем сказать 100,000 случайные сначала числа из массива. Чтобы избежать случайного доступа, который может быть медленным, мы добавляем скажем, 400 случайных групп из 250 последовательных чисел. С этим случайным выбор, мы можем быть совершенно уверены, что очень немногие из оставшихся чисел находятся в верхней сотне, поэтому время выполнения будет очень близко к простому циклу сравнения миллиарда чисел с некоторым максимальным значением.

    поиск лучших 100 из миллиарда чисел лучше всего сделать с помощью мин-кучи 100 элементов.

    сначала простейшая мин-куча с первыми 100 встреченными числами. min-heap будет хранить наименьшее из первых 100 чисел в корне (вверху).

    теперь, когда вы идете по остальным числам, сравните их только с корнем (наименьшим из 100).

    Если новое число встречается больше, чем корень min-кучи замените корень на это номер в противном случае игнорировать его.

    в рамках вставки нового числа в min-heap наименьшее число в куче придет наверх (root).

    Как только мы пройдем через все числа, у нас будет самое большое 100 чисел в Мин-куче.

    Я написал простое решение на Python в случае, если кто-то заинтересован. Он использует bisect модуль и временный список возврата, который он сохраняет отсортированным. Это похоже на реализацию очереди приоритетов.

    использование со 100,000,000 элементами и худшим случаем ввода, который является отсортированным списком:

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

    Я вижу много дискуссий O(N), поэтому я предлагаю что-то другое только для мысленного упражнения.

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

    Мастер Йода рекомендует:  Ошибки реализации постраничной навигации на сайте

    ! Посмотрите, заполняет ли какой-либо механизм заполнения списка этот список в определенном порядке. Находятся ли они в четко определенной схеме, где вы можете с уверенностью знать, что наибольшая величина чисел будет найдена в определенной области списка или на определенном интервале? Там может быть какая-то закономерность. Если это так, например, если они гарантированно находятся в каком-то нормальном распределении с характерным горбом посередине, всегда имеют повторяющиеся восходящие тенденции среди определенных подмножеств, имеют длительный всплеск в какое-то время T в середине набора данных, например, возможно, частота инсайдерской торговли или отказа оборудования, или, возможно, просто имеют » всплеск» каждое N-е число как и при анализе сил после катастрофы, вы можете значительно уменьшить количество записей, которые вам нужно проверить.

    в любом случае есть пища для размышлений. Возможно, это поможет вам дать будущим интервьюерам продуманный ответ. Я знаю, что был бы впечатлен, если бы кто — то задал мне такой вопрос в ответ на такую проблему-он сказал бы мне, что они думают об оптимизации. Просто признайте, что не всегда есть возможность оптимизировать.

    Урок 28
    Числа в памяти компьютера

    Основные темы параграфа:

    — представление целых чисел;
    — размер ячейки и диапазон значений чисел;
    — особенности работы компьютера с целыми числами;
    — представление вещественных чисел;
    — особенности работы компьютера с вещественными числами.

    Содержание урока

    Представление целых чисел

    Любая информация в памяти компьютера представляется в двоичном виде: последовательностью нулей и единиц. Исторически первым типом данных, с которыми стали работать компьютеры, были числа. Теперь это и числа, и тексты, и изображение, и звук. Работа с данными любого типа в конечном итоге сводится к обработке двоичных чисел — чисел, записываемых с помощью двух цифр, — 0 и 1.

    Поэтому современные компьютерные технологии называют цифровыми технологиями.

    В компьютере различаются два типа числовых величин: целые числа и вещественные числа. Различаются способы их представления в памяти компьютера.

    Представление целых чисел

    Часть памяти, в которой хранится одно число, будем называть ячейкой. Минимальный размер ячейки, в которой может храниться целое число, — 8 битов, или 1 байт. Получим представление десятичного числа 25 в такой ячейке. Для этого нужно перевести число в двоичную систему счисления. Как это делается, вы уже знаете.

    Теперь осталось «вписать» его в восьмиразрядную ячейку (записать так называемое внутреннее представление числа). Делается это так:

    Число записывается «прижатым» к правому краю ячейки (в младших разрядах). Оставшиеся слева разряды (старшие) заполняются нулями.

    Самый старший разряд — первый слева — хранит знак числа. Если число положительное, то в этом разряде ноль, если отрицательное — единица. Самому большому положительному целому числу соответствует следующий код:

    Чему он равен в десятичной системе? Можно расписать это число в развернутой форме и вычислить выражение. Но можно решить задачу быстрее. Если к младшему разряду этого числа прибавить единицу, то получится число 10000000. В десятичной системе оно равно 2 7 = 128. Значит:

    011111112 = 128 — 1 = 127.

    Максимальное целое положительное число, помещающееся в 8-разрядную ячейку, равно 127.

    Теперь рассмотрим представление целых отрицательных чисел. Как, например, в 8-разрядной ячейке памяти будет представлено число -25?

    Казалось бы, очевидным ответом является следующий: нужно в представлении числа 25 заменить старший разряд с 0 на 1. Но в компьютере все несколько сложнее.

    Для представления отрицательных целых чисел используется дополнительный код.

    Получить дополнительный код некоторого отрицательного числа -X можно по следующему алгоритму:

    1) записать внутреннее представление соответствующего ему положительного числа +Х — это мы уже умеем;
    2) записать обратный код полученного числа заменой во всех разрядах 0 на 1 и 1 на 0;
    3) к полученному числу прибавить 1.

    Определим по этим правилам внутреннее представление числа -2510 в восьмиразрядной ячейке:

    1) 00011001
    2) 11100110
    3) +1

    11100111 — это и есть представление числа -25.

    В результате выполнении такого алгоритма единица в старшем разряде получается автоматически. Она и является признаком отрицательного значения.

    Проверим полученный результат. Очевидно, что при сложении чисел +25 и -25 должен получиться ноль.

    1 1 1
    +
    1 1 1 1 1 1
    1

    Единица в старшем разряде, получаемая при сложении, выходит за границу ячейки и исчезает. В ячейке остается нолъ.

    Из этого примера теперь можно понять, почему представление отрицательного числа называется дополнительным кодом.

    Представление восьмиразрядного отрицательного числа -X дополняет представление соответствующего положительного числа +Х до значения 2 8 .

    Рассчитать медиану из миллиарда чисел

    Если у вас есть один миллиард чисел и сто компьютеров, каков наилучший способ найти медиану этих чисел?

    У меня есть одно решение:

    • Разделите набор поровну между компьютерами.
    • Сортировать их.
    • Найдите медианы для каждого набора.
    • Сортировка наборов по медиане.
    • Объединяйте два набора за раз от самого низкого до самого высокого среднего значения.

    Если у нас m1 то сначала объединяем Set1 и Set2 и в результирующем наборе мы можем отбросить все числа, меньшие, чем медиана Set12 (объединено). Так что в любой момент времени у нас есть равные по размеру наборы. Кстати, это не может быть сделано в параллельной манере. Есть идеи?

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

    Машина 1 должна называться «управляющей машиной», и в качестве аргумента она либо запускается со всеми данными и отправляет их равными частями другим 99 машинам, либо данные начинают равномерно распределяться между машинами, и отправляет 1/99 своих данных каждому из остальных. Перегородки не должны быть равными, просто близко.

    Каждая другая машина сортирует свои данные и делает это таким образом, чтобы в первую очередь находить более низкие значения. Так, например, быстрая сортировка, всегда сначала сортируя нижнюю часть раздела [*]. Он записывает свои данные обратно на управляющую машину в возрастающем порядке, как только может (используя асинхронный ввод-вывод для продолжения сортировки и, возможно, с включенным Nagle: немного поэкспериментируйте).

    Управляющая машина выполняет слияние с 99 путями по мере их поступления, но отбрасывает объединенные данные, просто сохраняя счет числа увиденных им значений. Он вычисляет медиану как среднее из значений 1/2 миллиарда и 1/2 миллиарда плюс одна.

    Это страдает от «самой медленной в стаде» проблемы. Алгоритм не может быть завершен до тех пор, пока сортировочная машина не отправит каждое значение меньше медианы. Существует разумная вероятность того, что одно такое значение будет достаточно высоким в пределах пакета данных. Таким образом, как только начальное разбиение данных завершено, расчетное время выполнения представляет собой комбинацию времени для сортировки 1/99 данных и отправки их обратно на компьютер управления, и времени для управления, считывающего 1/2 данных , «Комбинация» находится где-то между максимумом и суммой тех времен, вероятно, близко к максимуму.

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

    Поскольку сетевой ввод-вывод, вероятно, будет ограничен, могут быть некоторые приемы, которые вы можете воспроизвести, по крайней мере, для данных, возвращающихся на управляющую машину. Например, вместо отправки «1,2,3, .. 100», возможно, сортировочная машина могла бы отправить сообщение, означающее «100 значений меньше 101». Затем управляющая машина может выполнить модифицированное объединение, в котором она находит наименьшее из всех значений верхнего предела диапазона, а затем сообщает всем сортировочным машинам, что это было, так что они могут (а) сообщить управляющей машине, как множество значений для «подсчета» ниже этого значения и (b) возобновление отправки отсортированных данных с этой точки.

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

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

    [*] разрешение на доступный стек — ваш выбор, какую часть делать первым, ограничен, если у вас нет O (N) дополнительного пространства. Но если у вас достаточно свободного места, вы можете сделать свой выбор, а если вам не хватает места, вы можете, по крайней мере, использовать то, что у вас есть, чтобы обрезать некоторые углы, выполнив сначала небольшую часть для первых нескольких разделов.

    Нахождение наибольшего числа из трех

    Найти наибольшее число из трех. Если числа равны, то вывести любое из них.

    Входные данные

    В единственной строке входного файла INPUT.TXT записано три числа через пробел. Все числа целые, не меньше -10000 и не больше 10000.

    Выходные данные

    В файл OUTPUT.TXT выведите единственное число.

    Пример

    При копировании материалов обратная ссылка обязательна

    Алгоритмы поиска больших простых чисел

    Читайте также:

    1. Алгоритм вычитания чисел в десятичной системе счисления, теоретические положения , лежащие а его основе.
    2. Алгоритм вычитания чисел в десятичной системе счисления, теоретические положения , лежащие а его основе.
    3. Алгоритм двоичного поиска
    4. Алгоритм перевода дробных чисел из системы счисления с основанием q2 в десятичную систему.
    5. Алгоритм поиска кратчайших путей.
    6. Алгоритм поиска оптимального параметра системы стимулирования
    7. Алгоритм поиска промежуточных путей
    8. АЛГОРИТМ ПОИСКА ТОЧНОСТНЫХ ХАРАКТЕРИСТИК И СООТВЕТСТВУЮЩИХ ИМ ДОПУСКОВ 10 страница
    9. АЛГОРИТМ ПОИСКА ТОЧНОСТНЫХ ХАРАКТЕРИСТИК И СООТВЕТСТВУЮЩИХ ИМ ДОПУСКОВ 11 страница
    10. АЛГОРИТМ ПОИСКА ТОЧНОСТНЫХ ХАРАКТЕРИСТИК И СООТВЕТСТВУЮЩИХ ИМ ДОПУСКОВ 2 страница
    11. АЛГОРИТМ ПОИСКА ТОЧНОСТНЫХ ХАРАКТЕРИСТИК И СООТВЕТСТВУЮЩИХ ИМ ДОПУСКОВ 3 страница
    12. АЛГОРИТМ ПОИСКА ТОЧНОСТНЫХ ХАРАКТЕРИСТИК И СООТВЕТСТВУЮЩИХ ИМ ДОПУСКОВ 4 страница
    Подготовка к олимпиадам по информатике
    Методика подготовки
    «Золотые» алгоритмы
    Простые задачи для начинающих
    Олимпиадные задачи с решениями
    Книги
    Среда программирования
    Обучение программированию на С++
    Справочник по языку Pascal
    Обучение
    Подготовка к ЕГЭ
    Создание сайтов
    Уроки FrontPage
    Уроки Word 2003
    Создание игр на Delphi
    Печатаем вслепую

    Простым называется целое число, больше единицы, единственными множителями которого является 1 и оно само: оно не делится ни на одно другое число. Например, простыми являются 73, 2521, 2365347734339 и 2 756839 –1. Вся криптография с открытыми ключами основана на том факте, что существует бесконечно много простых чисел (Теорема Эвклида). В реальной криптографии используются большие простые числа, размер которых 512 бит и более.

    Существует приблизительно 10 151 простых чисел длиной 512 бит.

    Для чисел, близких n, вероятность того, что случайно выбранное число окажется простым равна 1/ln(n). Полное число простых чисел меньших n, приблизительно равно n/ ln(n).

    Может ли случится, что два человека слдучайно выберут одно и то же простое число? Из 10 151 простых чисел вероятность совпадения выбора значительно меньше, чем вероятность того, что в ваш компьютер ударит молния в тот момент когда вы выиграете в лотерею самый престижный приз.

    Если кто-то создаст базу данных всех простых чисел, может сможет он ее использовать для вскрытия алгоритмов с открытыми ключами? Пусть мы будем хранить один гигабайт информации на устройстве, весящем 1 грамм, то перечень простых чисел размерос до 512 бит имел такую массу, что она превысила бы предел Чандрасе́кара.Наша флэшка бы сколлапсировала бы в черную дыру и извлечь откуда данные все равно будет невозможно.

    Преде́л Чандрасе́кара — это верхний предел массы, при котором звезда может существовать как белый карлик. Если масса звезды превышает этот предел, то она становится нейтронной звездой.

    Алгоритмы поиска больших простых чисел можно разделить на две группы: алгоритмы перебора и вероятностные алгоритмы. К первой группе относятся алгоритм прямого перебора с проверкой делимости и «решето Эратосфена». Такие алгоритмы неэффективны для больших чисел, поскольку работают очень медленно. Вторая группа алгоритмов использует вероятностные оценки для определения простоты числа. Наиболее широкое распространение получили алгоритм Рабина-Миллера и тест Лемана [8].

    Алгоритм был разработан Гари Миллером в 1976 году и модифицирован Майклом Рабином в 1980 году

    Алгоритм Рабина Миллера предлагает следующую схему генерации простого числа:

    1. Выберите для проверки случайное число p. Вычислите b – число делений p – 1 на 2 (2b – это наибольшая степень числа 2, на которое делится
    p – 1). Затем вычислите m, такое что p = 1 + 2b * m.

    2. Выберите случайное число a 0 и z = 1, то p не является простым числом.

    6. Установите j = j + 1. Если j t , где t – это число итераций.

    Альтернативный алгоритм для поиска простых чисел был разработан Леманом.

    Вот последовательность действий при проверке простоты числа p:

    1. Выбрать случайное число а, причем a

    2. Вычислить k= a (p-1) div2 mod p;

    3. Если k ≠ 1 или k≠ (p-1), то рассматриваемое p не является простым.

    4. Если k =1 или k= (p-1), то вероятность того, что p не является простым, не более 50 процентов.

    5. Попытку (1) – (4) повторить т раз с различными случайными значениями a.

    Если результат вычислений равен 1 или (p–1), но не всегда равен 1, то p является простым числом с вероятностью ошибки 1/2 m .

    Два числа называются взаимно простыми, если у них нет общих множителей кроме 1. Иными словами, если наибольший общий делитель этих чисел равен 1. Если n является простым числом, то любое число от 1 до n–1 взаимно просто с n. Взаимно просты числа 15 и 32, 27 и 64. Одним из способов вычислить наибольший общий делитель двух чисел является алгоритм Эвклида, описанный в книге Элементы (300 год до н. э.)

    | следующая лекция ==>
    Алгоритм расчета контрольной суммы CRC32 | Алгоритмы возведения в степень в конечном поле

    Дата добавления: 2014-01-15 ; Просмотров: 1071 ; Нарушение авторских прав? ;

    Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет

    Опишите алгоритм для нахождения миллиона наименьших чисел в наборе из миллиарда чисел. Память компьютера позволяет хранить весь миллиард чисел

    Дан набор из N целых по­ло­жи­тель­ных чисел. Не­об­хо­ди­мо вы­брать из на­бо­ра про­из­воль­ное ко­ли­че­ство чисел так, чтобы их сумма была как можно боль­ше и при этом не де­ли­лась на 8. В от­ве­те нужно ука­зать ко­ли­че­ство вы­бран­ных чисел и их сумму, сами числа вы­во­дить не надо. Если по­лу­чить нуж­ную сумму не­воз­мож­но, счи­та­ет­ся, что вы­бра­но 0 чисел и их сумма равна 0.

    На­пи­ши­те эф­фек­тив­ную по вре­ме­ни и по па­мя­ти про­грам­му для ре­ше­ния этой за­да­чи.

    Про­грам­ма счи­та­ет­ся эф­фек­тив­ной по вре­ме­ни, если при уве­ли­че­нии ко­ли­че­ства ис­ход­ных чисел N в k раз время ра­бо­ты про­грам­мы уве­ли­чи­ва­ет­ся не более чем в k раз.

    Про­грам­ма счи­та­ет­ся эф­фек­тив­ной по па­мя­ти, если па­мять, не­об­хо­ди­мая для хра­не­ния всех пе­ре­мен­ных про­грам­мы, не пре­вы­ша­ет 1 ки­ло­байт и не уве­ли­чи­ва­ет­ся с ро­стом N.

    Мак­си­маль­ная оцен­ка за пра­виль­ную (не со­дер­жа­щую син­так­си­че­ских оши­бок и да­ю­щую пра­виль­ный ответ при любых до­пу­сти­мых вход­ных дан­ных) про­грам­му, эф­фек­тив­ную по вре­ме­ни и по па­мя­ти, — 4 балла.

    Мак­си­маль­ная оцен­ка за пра­виль­ную про­грам­му, эф­фек­тив­ную толь­ко по вре­ме­ни или толь­ко по па­мя­ти, — 3 балла.

    Мак­си­маль­ная оцен­ка за пра­виль­ную про­грам­му, не удо­вле­тво­ря­ю­щую тре­бо­ва­ни­ям эф­фек­тив­но­сти, — 2 балла.

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

    Перед тек­стом про­грам­мы крат­ко опи­ши­те ал­го­ритм ре­ше­ния. Ука­жи­те ис­поль­зо­ван­ный язык про­грам­ми­ро­ва­ния и его вер­сию.

    Опи­са­ние вход­ных и вы­ход­ных дан­ных

    В пер­вой стро­ке вход­ных дан­ных задаётся ко­ли­че­ство чисел N (1 ≤ N ≤ 1000).

    В каж­дой из по­сле­ду­ю­щих N строк за­пи­са­но одно на­ту­раль­ное число, не пре­вы­ша­ю­щее 10 000.

    При­мер вход­ных дан­ных:

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

    При­мер вы­ход­ных дан­ных для при­ведённого выше при­ме­ра вход­ных дан­ных:

    В дан­ном слу­чае из пред­ло­жен­но­го на­бо­ра нужно вы­брать два числа (2 и 5), их сумма равна 7.

    Если сумма всех дан­ных чисел не крат­на 8, нужно про­сто взять все числа.

    Если сумма крат­на 8, нужно уда­лить из неё ми­ни­маль­но воз­мож­ный эле­мент — наи­мень­шее из за­дан­ных чисел, не крат­ное 8. Если таких чисел нет (все числа в на­бо­ре крат­ны 8), то по­лу­чить тре­бу­е­мую сумму не­воз­мож­но, в этом слу­чае по усло­вию за­да­чи ответ счи­та­ет­ся рав­ным нулю.

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

    Ниже при­ве­де­на ре­а­ли­зу­ю­щая этот ал­го­ритм про­грам­ма на языке Пас­каль (ис­поль­зо­ва­на вер­сия PascalABC)

    При­мер пра­виль­ной, но не­эф­фек­тив­ной про­грам­мы на языке Пас­каль.

    Разработка способа представления длинных чисел в памяти компьютера

    Рубрика: Информационные технологии

    Дата публикации: 16.11.2020 2020-11-16

    Статья просмотрена: 510 раз

    Библиографическое описание:

    Коптенок Е. В., Кузин А. В., Шумилин Т. Б., Соколов М. Д. Разработка способа представления длинных чисел в памяти компьютера // Молодой ученый. — 2020. — №46. — С. 26-30. — URL https://moluch.ru/archive/180/46418/ (дата обращения: 10.11.2020).

    Под длинной арифметикой понимаются выполняемые с помощью вычислительной машины арифметические операции над числами, разрядность которых превышает длину машинного слова данной вычислительной машины. Эти операции реализуются не аппаратно, а программно, с использованием базовых аппаратных средств работы с числами меньших порядков. Частный случай — арифметика произвольной точности — относится к арифметике, в которой длина чисел ограничена только объёмом доступной памяти.

    Длинная арифметика применяется в различных областях компьютерных технологий. Например, в криптографии большинство систем подписывания и шифрования данных используют целочисленную арифметику по модулю m, где m — очень большое натуральное число, не обязательно простое. Например, при реализации метода шифрования RSA, криптосистемы Рабина или схемы Эль-Гамаля требуется обеспечить точность результатов умножения и возведения в степень порядка 10309. В математическое и финансовое ПО результат вычисления на бумаге должен совпадать с результатом работы компьютера с точностью до последнего разряда.

    Наиболее мощные калькуляторы, реализующие длинную арифметику, имеют ряд недостатков:

    1. Стоимость: например, калькулятор «eCalc» стоит от 45 евро. Калькуляторы, обладающие широким функционалом и близкой к неограниченной точности вычислений, являются коммерческими продуктами;
    2. Ограниченный функционал: как правило, бесплатные онлайн калькуляторы имеют ряд существенных ограничений, связанных либо с точностью вычислений, либо с максимальным возможным значением. Например, один из популярных онлайн калькуляторов “web calc”, имеющий большой набор действий над числами, строить графики и вычислять значения простых функций, не находит факториалы чисел больше 200. Также данный калькулятор не дает возможности задать точность числа (устанавливается автоматически). Соответственно, если нам нужна более высокая точность, вычисления будут нести погрешность.
    3. Большинство бесплатных калькуляторов работают лишь с целыми числами или с ограниченной точностью.
    4. Закрытое ПО: большинство программ, реализующих вычисления неограниченно больших чисел с любой точностью разрабатываются для шифрования и кодирования и не находятся в открытом доступе.

    Разработанный программный продукт устраняет большинство недостатков доступных аналогов:

    – Работа с числами практически неограниченной длины. В отличие от большинства аналогов, программа способна манипулировать десятичными числами до 10 10^9 ;

    – Реализация всех базовых арифметических действий над числами, в том числе и деления с остатком необходимой точности;

    – Возможность работы с дробными числами.

    – Данный продукт способен преобразовать выражение произвольной длины, имеющее до 27 переменных (переменные в выражении могут повторяться). Операции, которые могут быть использованы в выражении:

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

    – сложение/вычитание чисел или выражений;

    – Умножение двух чисел с заданной конечной точностью;

    – Деление двух чисел. Может быть произведено с заданной точностью, а так же выделение целой части или остатка от деления;

    – Возведение в степень, причем показатель степени также может быть длинным числом;

    – Факториал числа или выражения;

    Операции возведения в степень и факториала имеют свои ограничения, так как в данной версии продукта результат не может превышать 10 10^9 .Например, наибольшее число, факториал которого может быть найден — 6000. Среди бесплатных аналогов вычисление факториала такой величины обнаружено не было.

    Также программа сохраняет результат в текстовый документ для дальнейшего использования и, наоборот, позволяет считать выражение и значение переменных из файла;

    Хранение числа впамяти

    Из различных способов представления длинных чисел в памяти компьютера для разработки программного продукта был выбран и доработан принцип поразрядного хранения, так как такие методы, как метод факторизации или разложения на простые модули ограничивают функционал программы (нет возможности работать с дробными числами, производить деление чисел).

    Так как метод поразрядного хранения может привести к повышенному расходу памяти, а также к ограничению длины числа, было принято решение хранить десятичное число в системе счисления с основанием 10000 (Рис. 1):

    Рис. 1. Принципиальная схема хранения длинного числа

    Хранение числа реализовано в двусвязном списке, к ссылкам на начало и конец числа была добавлена ссылка на младший разряд целой части. Благодаря этому ест возможность задать любую точность для хранимого числа. Число 10000 было выбрано за размер одной ячейки во избежание перегрузки элемента списка при выполнении промежуточных расчетов. Во время вычисления выражения максимальное промежуточное значение должно помещаться в переменную типа int. Максимальное промежуточное значение для числа 9999 будет 9999*9999, что помещается в переменную типа int.

    Сложение ивычитание

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

    Стоит отметить, что для ускорения вычислений при вычитании строка выражения преобразуется следующим образом: если производится вычитание, то все последующие знаки сложения и вычитания (равного приоритета) инвертируются. Например, при вводе выражения ab*(c+a)-a+d программа при выполнении первого вычитания фактически изменит выражение и приведет его к виду a-(b*(c+a)-a+d).

    И сложение, и вычитание, выполняется после приведения в соответствие дробной части обоих чисел путем добавления нулевых разрядов в дробную часть числа, у которого она короче. После этого выполняется поразрядное сложение с проверкой переполнения разрядов (Рис. 2).

    Рис. 2. Реализация операции сложения

    Умножение

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

    Умножить первый множитель на младший разряд второго с проверкой переполнения ячеек;

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

    Отделить дробную часть путем перемещения нужной ссылки (Рис. 3).

    Рис. 3. Реализация операции умножения

    Деление

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

    1. Ссылки на младший разряд целой части сдвигаются к младшему разряду до тех пор, пока у одного из чисел данные ссылки не совпадут (фактически, того же эффекта можно достигнуть, умножая оба числа на 10000, пока одно из них не станет целым, так как 0,16/0,04=16/4);
    2. Из делимого выделяется количество разрядов, равное количеству разрядов делителя (если изначально у делимого меньше разрядов, чем у делителя, целая часть будет равна нулю);
    3. Выполняется деление получившихся чисел;
    4. К делимому прибавляем старший из не использованных ранее разрядов и повторяем п.3;
    5. Когда число-делитель становится больше делимого, начинаем вычислять остаток: к делимому добавляем один пустой разряд (эквивалентно умножению на 10000) и производим деление;
    6. Повторяем п.5 до тех пор, пока не будет достигнута заданная точность или остаток не будет равен 0 (Рис. 4)

    Рис. 4. Реализация операции деления

    Возведение встепень

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

    1. Если показатель степени четный, разделить показатель степени на два, выражение представить в виде a 2 n =a n *a n ;
    2. Если показатель степени нечетный, то представить выражение в виде a 2 n +1 =a2 n *a, для a 2 n повторить шаг 1;

    В результате выполнения такого алгоритма получится развернутая запись числа, которая громоздкая на вид, но дает колоссальный эффект при вычислении выражений типа a n ; Например, число a 10 можно представить как ((a 2 ) 2 *a) 2 . Программа выполнит фактически 4 операции умножения вместо десяти. В целом сложность такого алгоритма оценивается как О=log2n, где n — показатель степени, что гораздо эффективнее последовательного умножения (сложность алгоритма O=n);

    Факториал

    Факториал числа вычисляется путем рекурсивного умножения: n!=n*(n-1)! и т. д. Найти более быстрые алгоритмы для длинных чисел пока не удалось.

    Пример применения алгоритма

    Результатом проделанной работы стала программа, которая позволяет рассчитать выражение, содержащие до 27 переменных (которые могут повторяться внутри выражения) с расчетом результата, количество разрядов которого в десятичной системе может достигать 10 10^9 . Точность вычисления (количество знаков после запятой) задается пользователем. Над числами могут производиться все базовые арифметические операции (сложение, вычитание, умножение, деление, возведение в степень, факториал). Все функции для записи числа и выполнения расчетов вынесены в библиотеку, которую можно встроить в сторонний проект.

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

    Как уже было написано выше, сложно найти значение факториалов для чисел больших, чем 200. Данная программа позволяет рассчитывать факториал чисел до 6000 (Рис. 5).

    Так как калькуляторов, рассчитывающих такие числа, найдено не было, в Интернете была найдена текстовая запись числа и результат был сверен посимвольно, так как вышло число, количество разрядов которого превышает 20 000 символов.

    Рис. 5. Реализация возведения в факториал большого числа

    Данная библиотека функций может послужить фундаментом для разработки более мощного калькулятора с более широкой областью применения. Целью разработки данной программы является возможность производить вычисления с абсолютно любой точностью, ограниченной лишь памятью компьютера, так как на данный момент предельная длина числа ограничена программно (10 10:9 разрядов). Преодоление данного ограничения поможет перейти к арифметике произвольной точности.

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

    Следующей перспективой развития является более гибкая обработка выражений, которая позволит использовать произвольное количество переменных в выражении (на данный момент их количество ограничено буквами латинского алфавита).

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

    Опишите алгоритм для нахождения миллиона наименьших чисел в наборе из миллиарда чисел. Память компьютера позволяет хранить весь миллиард чисел

    54138 просмотра

    30 ответа

    1984 Репутация автора

    Недавно я посетил интервью, где меня попросили «написать программу, чтобы найти 100 самых больших чисел из массива в 1 миллиард чисел».

    Я был в состоянии дать только грубое решение, которое должно было отсортировать массив за O (nlogn) сложность времени и взять последние 100 чисел.

    Интервьюер искал лучшую временную сложность, я попробовал пару других решений, но не смог ответить на него. Есть ли лучшее решение сложности времени?

    Ответы (30)

    70 плюса

    1162 Репутация автора

    Вы можете перебирать числа, которые занимают O (n)

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

    Минут этой круговой очереди — ваше новое значение сравнения. Продолжайте добавлять в эту очередь. Если заполнено, извлеките минимум из очереди.

    322 плюса

    1740 Репутация автора

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

    РЕДАКТИРОВАТЬ: как отметил Dev, с приоритетной очереди, реализованной с кучей, сложность вставки в очередь O(logN)

    В худшем случае вы получите что лучше, чем billionlog2(100) billion log2(billion)

    В общем, если вам нужно наибольшее число K из набора из N чисел, сложность O(NlogK) скорее, чем O(NlogN) , это может быть очень значительным, когда K очень мало по сравнению с N.

    EDIT2:

    Ожидаемое время этого алгоритма довольно интересно, так как на каждой итерации вставка может происходить или не происходить. Вероятность того, что i-е число будет вставлено в очередь, — это вероятность того, что случайная величина будет больше, чем, по крайней мере, i-K случайные переменные из того же распределения (первые k чисел автоматически добавляются в очередь). Мы можем использовать статистику заказов (см. Ссылку ), чтобы рассчитать эту вероятность. Например, предположим, что числа были случайным образом выбраны равномерно <0, 1>, ожидаемое значение (iK) -ого числа (из i-х чисел) равно (i-k)/i вероятности того, что случайная величина будет больше этого значения 1-[(i-k)/i] = k/i .

    Таким образом, ожидаемое количество вставок составляет:

    И ожидаемое время работы может быть выражено как:

    ( k время для генерации очереди с первыми k элементами, затем n-k сравнений и ожидаемого количества вставок, как описано выше, каждая занимает среднее log(k)/2 время)

    Обратите внимание , что при N очень большой по сравнению с K , это выражение намного ближе к n чем NlogK . Это несколько интуитивно понятно, так как в случае вопроса даже после 10000 итераций (что очень мало по сравнению с миллиардом) вероятность того, что число будет вставлено в очередь, очень мала.

    5 плюса

    85 Репутация автора

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

    15 плюса

    7728 Репутация автора

    Вы можете использовать алгоритм быстрого выбора, чтобы найти число по индексу (по порядку) [billion-101], а затем перебрать числа и найти числа, которые больше этого числа.

    Время этого алгоритма: 2 XO (N) = O (N) (средняя производительность по случаю)

    Второй вариант, предложенный Томасом Юнгблутом :

    Использование Heap строит кучу MAX будет принимать O (N), то топ 100 Макс число будет находиться в верхней части кучи, все , что вам нужно , это получить их из кучи (100 XO (Log (N)).

    Время этого алгоритма: O (N) + 100 XO (Log (N)) = O (N)

    10 плюса

    10756 Репутация автора

    Хотя другое решение для быстрого выбора было отклонено, факт остается фактом, что быстрый выбор найдет решение быстрее, чем использование очереди размером 100. У Quickselect ожидаемое время выполнения 2n + o (n), с точки зрения сравнений. Очень просто реализация будет

    Это займет 3n + o (n) сравнений в среднем. Более того, это можно сделать более эффективным, используя тот факт, что быстрый выбор оставит 100 самых больших элементов в массиве в 100 самых правых местах. Таким образом, время выполнения может быть улучшено до 2n + o (n).

    Существует проблема, что, как ожидается, время работы, и не худший случай, но с использованием стратегии приличной выбора поворота (например, выбрать 21 элементов в случайном порядке, и выбрать медиану этих 21 в качестве оси), то число сравнений может быть гарантируется с высокой вероятностью не более (2 + c) n для сколь угодно малой постоянной c.

    Фактически, используя оптимизированную стратегию выборки (например, выборку элементов sqrt (n) случайным образом и выбор 99-го процентиля), время выполнения может быть уменьшено до (1 + c) n + o (n) для сколь угодно малого c (при условии, что K, количество элементов, которые будут выбраны, o (n)).

    С другой стороны, использование очереди размером 100 потребует O (log (100) n) сравнений, а база журналов 2 из 100 приблизительно равна 6,6.

    Если мы подумаем об этой проблеме в более абстрактном смысле, выбирая самые большие элементы K из массива размера N, где K = o (N), но оба K и N уходят в бесконечность, тогда время работы версии быстрого выбора будет O (N) и версия очереди будет O (N log K), поэтому в этом смысле быстрый выбор также асимптотически лучше.

    В комментариях было упомянуто, что решение очереди будет запущено в ожидаемое время N + K log N на случайном входе. Конечно, предположение о случайном вводе никогда не будет действительным, если вопрос не сформулирован явно. Решение очереди может быть сделано для обхода массива в случайном порядке, но это повлечет за собой дополнительные затраты на N вызовов генератора случайных чисел, а также либо перестановку всего входного массива, либо выделение нового массива длиной N, содержащего случайные индексы.

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

    Автор: mrip Размещён: 07.10.2013 03:42

    16 плюса

    17613 Репутация автора

    Моей непосредственной реакцией на это было бы использование кучи, но есть способ использовать QuickSelect, не сохраняя все входные значения под рукой одновременно.

    Создайте массив размером 200 и заполните его первыми 200 входными значениями. Запустите QuickSelect и откажитесь от низких 100, оставив вам 100 свободных мест. Прочитайте следующие 100 входных значений и снова запустите QuickSelect. Продолжайте до тех пор, пока вы не выполните все входные данные партиями по 100.

    В конце у вас есть 100 лучших значений. Для значений N вы запустили QuickSelect примерно N / 100 раз. Стоимость каждого быстрого выбора примерно в 200 раз превышает некоторую постоянную, поэтому общая стоимость в 2N раза превышает некоторую постоянную. Это выглядит линейно по размеру входных данных для меня, независимо от размера параметра, который я собираюсь установить равным 100 в этом объяснении.

    плюса

    10905 Репутация автора

    Я написал простое решение на Python на случай, если кому-то будет интересно. Он использует bisect модуль и временный список возврата, который он сохраняет отсортированным. Это похоже на реализацию очереди с приоритетами.

    Использование с 100 000 000 элементов и вводом в худшем случае, который представляет собой отсортированный список:

    Потребовалось около 40 секунд, чтобы рассчитать это для 100 000 000 элементов, поэтому я боюсь сделать это за 1 миллиард. Чтобы быть справедливым, хотя, я кормил его входом наихудшего случая (по иронии судьбы, массив уже отсортирован).

    1 плюс

    1302 Репутация автора

    Самое простое решение — сканировать массив из миллиардов чисел и хранить 100 самых больших значений, найденных до сих пор, в небольшом буфере массива без какой-либо сортировки и запоминать наименьшее значение этого буфера. Сначала я подумал, что этот метод был предложен fordprefect, но в комментарии он сказал, что он предполагает реализацию структуры данных из 100 чисел в виде кучи. Всякий раз, когда обнаруживается новое число, которое больше минимума в буфере, перезаписывается новым найденным значением, и в буфере снова выполняется поиск текущего минимума. Если числа в массиве миллиардов чисел распределены случайным образом большую часть времени, значение из большого массива сравнивается с минимумом маленького массива и отбрасывается. Только для очень очень маленькой доли числа значение должно быть вставлено в маленький массив. Таким образом, разница в манипулировании структурой данных, содержащей маленькие числа, может игнорироваться. Для небольшого числа элементов трудно определить, является ли использование очереди приоритетов на самом деле более быстрым, чем использование моего наивного подхода.

    Я хочу оценить количество вставок в небольшой буфер массива из 100 элементов при сканировании массива из 10 ^ 9 элементов. Программа сканирует первые 1000 элементов этого большого массива и должна вставить в буфер не более 1000 элементов. Буфер содержит 100 элементов из 1000 отсканированных элементов, то есть 0,1 отсканированного элемента. Таким образом, мы предполагаем, что вероятность того, что значение из большого массива больше, чем текущий минимум буфера, составляет около 0,1. Такой элемент должен быть вставлен в буфер. Теперь программа сканирует следующие 10 ^ 4 элементов из большого массива. Поскольку минимум буфера будет увеличиваться каждый раз, когда вставляется новый элемент. Мы подсчитали, что соотношение элементов больше нашего текущего минимума составляет около 0,1, и поэтому для вставки требуется 0,1 * 10 ^ 4 = 1000 элементов. На самом деле ожидаемое количество элементов, которые вставляются в буфер, будет меньше. После сканирования этих 10 ^ 4 элементов доля чисел в буфере составит около 0,01 от сканированных элементов. Поэтому при сканировании следующих 10 ^ 5 чисел мы предполагаем, что в буфер будет вставлено не более 0,01 * 10 ^ 5 = 1000. Продолжая эту аргументацию, мы вставили около 7000 значений после сканирования 1000 + 10 ^ 4 + 10 ^ 5 + . + 10 ^ 9

    10 ^ 9 элементов большого массива. Поэтому при сканировании массива с 10 ^ 9 элементами случайного размера мы ожидаем не более 10 ^ 4 (= 7000 округленных) вставок в буфер. После каждой вставки в буфер должен быть найден новый минимум. Если буфер представляет собой простой массив, нам нужно 100 сравнений, чтобы найти новый минимум. Если буфер представляет собой другую структуру данных (например, кучу), нам нужно как минимум 1 сравнение, чтобы найти минимум. Чтобы сравнить элементы большого массива, нам нужно 10 ^ 9 сравнений. Таким образом, в целом нам нужно около 10 ^ 9 + 100 * 10 ^ 4 = 1.001 * 10 ^ 9 сравнений при использовании массива в качестве буфера и как минимум 1.000 * 10 ^ 9 сравнений при использовании другого типа структуры данных (например, кучи) , Таким образом, использование кучи приносит только 0,1% прироста, если производительность определяется числом сравнений. Но какова разница во времени выполнения между вставкой элемента в кучу из 100 элементов и заменой элемента в массиве из 100 элементов и поиском его нового минимума? 000 * 10 ^ 9 сравнений при использовании другого типа структуры данных (например, кучи). Таким образом, использование кучи приносит только 0,1% прироста, если производительность определяется числом сравнений. Но какова разница во времени выполнения между вставкой элемента в кучу из 100 элементов и заменой элемента в массиве из 100 элементов и поиском его нового минимума? 000 * 10 ^ 9 сравнений при использовании другого типа структуры данных (например, кучи). Таким образом, использование кучи приносит только 0,1% прироста, если производительность определяется числом сравнений. Но какова разница во времени выполнения между вставкой элемента в кучу из 100 элементов и заменой элемента в массиве из 100 элементов и поиском его нового минимума?

    На теоретическом уровне: сколько сравнений необходимо для вставки в кучу. Я знаю, что это O (log (n)), но насколько велик постоянный фактор? я

    На машинном уровне: Какое влияние оказывают кэширование и прогноз ветвления на время выполнения вставки кучи и линейного поиска в массиве.

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

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

    132 плюса

    1361 Репутация автора

    Если об этом спрашивают в интервью, я думаю, что интервьюер, вероятно, хочет видеть ваш процесс решения проблем, а не только ваши знания алгоритмов.

    Описание довольно общее, так что, возможно, вы можете задать ему диапазон или значение этих чисел, чтобы прояснить проблему. Это может произвести впечатление на интервьюера. Если, например, эти цифры соответствуют возрасту людей внутри страны (например, Китая), то это гораздо более простая проблема. С разумным допущением, что никто не живет старше 200 лет, вы можете использовать массив int размером 200 (может быть, 201), чтобы подсчитать количество людей одного возраста за одну итерацию. Здесь индекс означает возраст. После этого это кусок пирога, чтобы найти 100 наибольшее число. Кстати, этот алгоритм называется счетной сортировкой .

    В любом случае, сделать интервью более конкретным и понятным — это хорошо для вас.

    Автор: jin Размещён: 08.10.2013 06:04

    плюса

    12516 Репутация автора

    Я вижу много O (N) обсуждений, поэтому я предлагаю что-то другое только для мыслительного упражнения.

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

    Тем не мение! Посмотрите, заполняет ли какой-либо механизм заполнения списков этот список в определенном порядке. Находятся ли они в четко определенной схеме, в которой вы можете точно знать, что наибольшая величина чисел будет найдена в определенной области списка или в определенном интервале? Там может быть образец для этого. Если это так, например, если они гарантированно находятся в каком-то нормальном распределении с характерным горбом в середине, всегда имеют повторяющиеся восходящие тренды среди определенных подмножеств, имеют длительный всплеск в некоторый момент времени T в середине данных установите, например, частоту случаев инсайдерской торговли или отказа оборудования, или, может быть, просто поставьте «пик» на каждое N-е число, так как при анализе сил после катастрофы вы можете значительно сократить количество проверяемых записей.

    В любом случае, есть пища для размышлений. Может быть, это поможет вам дать будущим интервьюерам вдумчивый ответ. Я знаю, что был бы впечатлен, если бы кто-то задал мне такой вопрос в ответ на такую ​​проблему — это бы сказало мне, что они думают об оптимизации. Просто осознайте, что не всегда есть возможность оптимизировать.

    32 плюса

    1593 Репутация автора

    Я понял, что это помечено как «алгоритм», но выбрасывает некоторые другие варианты, так как это, вероятно, также должно быть помечено как «интервью».

    Каков источник 1 миллиарда чисел? Если бы это была база данных, то «выбор значения из порядка таблиц по значению desc limit 100» отлично справился бы с этой задачей — могут существовать различия в диалектах.

    Это одноразовое или что-то, что будет повторяться? Если повторяется, как часто? Если это одноразовый файл и данные находятся в файле, то ‘cat srcfile | сортировать (варианты по необходимости) | head -100 ‘позволит вам быстро выполнять продуктивную работу, за которую вам платят, пока компьютер выполняет эту тривиальную работу.

    Если это будет повторяться, вы посоветуете выбрать какой-нибудь достойный подход, чтобы получить первоначальный ответ и сохранить / кэшировать результаты, чтобы вы могли постоянно иметь возможность сообщать о первых 100.

    Наконец, есть это соображение. Вы ищете работу начального уровня и проводите собеседования с вычурным менеджером или будущим коллегой? Если это так, то вы можете отказаться от всех подходов, описывающих относительные технические плюсы и минусы. Если вы ищете более управленческую работу, то подходите к ней, как это сделал бы менеджер, связанный с затратами на разработку и обслуживание решения, и говорите «большое спасибо», и уходите, если это интервьюер хочет сосредоточиться на мелочах CS , У него и у вас вряд ли будет большой потенциал продвижения вперед.

    Удачи на следующем интервью.

    -1 плюса

    6479 Репутация автора

    Я знаю, что это может быть похоронено, но вот моя идея для вариации на radix MSD .

    Функция getMsdIdx(int num) будет возвращать индекс самой значимой цифры (не ноль). Функция getMsd(int num) вернет самую значимую цифру. Функция removeMSD(int num) удаляет наиболее значимую цифру из числа и возвращает номер (или возвращает ноль, если после удаления самой значащей цифры ничего не осталось).

    После того, как это будет сделано, все, что осталось, это пройти mynums 100 верхних цифр. Это было бы что-то вроде:

    Я должен отметить, что, хотя вышеприведенное выглядит так, как будто оно имеет высокую временную сложность, оно действительно будет только вокруг O(7*100) .

    Краткое объяснение того, что это пытается сделать: По сути, эта система пытается использовать каждую цифру в 2-мерном массиве, основываясь на индексе цифры в числе и ее значении. Он использует их как индексы, чтобы отслеживать, сколько чисел этого значения было вставлено в массив. Когда 100 достигнуто, оно закрывает все «нижние ветви».

    Время этого алгоритма примерно такое O(billion*log(16)*7)+O(100) . Я могу ошибаться по этому поводу. Также весьма вероятно, что это требует отладки, поскольку это довольно сложно, и я просто написал это на макушке.

    РЕДАКТИРОВАТЬ: Downvotes без объяснения причин не помогают. Если вы считаете этот ответ неверным, оставьте комментарий, почему. Я уверен, что StackOverflow даже скажет вам сделать это, когда вы понижаете голос.

    4 плюса

    388 Репутация автора

    (1) куча (приоритетная очередь)

    Поддерживайте минимальную кучу размером 100. Пройдите через массив. Как только элемент станет меньше первого элемента в куче, замените его.

    (2) Карта-уменьшенная модель.

    Это очень похоже на пример подсчета слов в hadoop. Задание на карте: подсчитайте частоту или время каждого элемента. Уменьшить: получить верхний элемент К.

    Обычно я бы дал рекрутеру два ответа. Дайте им все, что они хотят. Конечно, кодирование с уменьшением карты будет трудоемким, потому что вы должны знать все точные параметры. Не вредно практиковать это. Удачи.

    Автор: Chris Su Размещён: 09.10.2013 12:27

    1 плюс

    870 Репутация автора

    Вдохновленный ответом @ron teller, вот программа на Си, которая делает то, что вы хотите.

    На моей машине (ядро i3 с быстрым SSD) это занимает 25 секунд и 1724 сортировки. Я создал двоичный файл dd if=/dev/urandom/ count=1000000000 bs=1 для этого запуска.

    Очевидно, что есть проблемы с производительностью при чтении только 4 байтов за раз — с диска, но это ради примера. С положительной стороны, очень мало памяти требуется.

    Автор: ldrumm Размещён: 09.10.2013 12:31

    плюса

    5102 Репутация автора

    Создать пустой список из 100 пустых слотов

    Для каждого номера в списке ввода:

    Если число меньше первого, пропустите

    В противном случае замените его на этот номер

    Затем нажмите номер через соседний своп; пока он не станет меньше следующего

    Примечание: если log(input-list.size) + c , то оптимальным способом является сортировка списка ввода, а затем разбить первые 100 элементов.

    плюса

    9 Репутация автора

    Сначала создайте массив из 100 дюймов, инициализируйте первый элемент этого массива как первый элемент из N значений, отследите индекс текущего элемента с помощью другой переменной, назовите его CurrentBig.

    Итерация по значениям N

    когда закончите, выведите массив M из CurrentBig 100 раз по модулю 100 🙂 Для ученика: убедитесь, что последняя строка кода не превосходит правильные данные перед выходом кода

    плюса

    79 Репутация автора

    Другой алгоритм O (n) —

    Алгоритм находит наибольшее 100 по исключению

    Рассмотрим все миллионы чисел в их двоичном представлении. Начните с самого значительного бита. Выяснение, является ли MSB 1, может быть сделано умножением логической операции с соответствующим числом. Если в этих миллионах больше 100 единиц, то остальные числа с нулями исключите. Теперь из оставшихся чисел перейдем к следующему наиболее значимому биту. вести подсчет количества оставшихся чисел после исключения и продолжать до тех пор, пока это число больше 100.

    Основная логическая операция может выполняться параллельно на графических процессорах.

    плюса

    125 Репутация автора

    Я бы выяснил, у кого было время собрать миллиард чисел в массив и уволить его. Должен работать на правительство. По крайней мере, если бы у вас был связанный список, вы могли бы вставить число в середину, не сдвигая полмиллиарда, чтобы освободить место. Еще лучше Btree позволяет бинарный поиск. Каждое сравнение устраняет половину вашей суммы. Алгоритм хеширования позволит вам заполнить структуру данных как шахматную доску, но не так хорошо для разреженных данных. Лучше всего иметь массив решений из 100 целых чисел и отслеживать минимальное число в массиве решений, чтобы вы могли заменить его, когда натолкнетесь на большее число в исходном массиве. Вам придется посмотреть на каждый элемент в исходном массиве, предполагая, что он не отсортирован с самого начала.

    -1 плюса

    5597 Репутация автора

    Этот код предназначен для поиска N самых больших чисел в несортированном массиве .

    Это не может быть эффективным, но делает работу.

    Надеюсь это поможет

    плюса

    10107 Репутация автора

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

    4 плюса

    10107 Репутация автора

    Очень простым решением было бы перебрать массив 100 раз. Который есть O(n) .

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

    плюса

    4880 Репутация автора

    1. Используйте n-й элемент, чтобы получить 100-й элемент O (n)
    2. Повторяйте второй раз, но только один раз и выводите каждый элемент, который больше, чем этот конкретный элемент.

    Пожалуйста, обратите внимание esp. второй шаг может быть легко вычислен параллельно! И это также будет эффективно, когда вам нужен миллион самых больших элементов.

    Автор: math Размещён: 11.10.2013 08:01

    плюса

    11 Репутация автора

    Это вопрос от Google или некоторых других гигантов отрасли. Возможно, следующий код — правильный ответ, ожидаемый вашим интервьюером. Стоимость времени и стоимость пространства зависят от максимального числа во входном массиве. Для 32-битного ввода массива int, максимальная стоимость пространства составляет 4 * 125M байт, стоимость времени — 5 * млрд.

    Автор: Su Xiang Размещён: 13.10.2013 09:35

    1 плюс

    486 Репутация автора

    Алгоритм Biggest x элементов из n:

    Я назову возвращаемое значение LIST . Это набор элементов x (по моему мнению, это должен быть связанный список)

    • Первые элементы x берутся из пула «по мере их поступления» и сортируются в LIST (это делается за постоянное время, поскольку x рассматривается как постоянное время — O (x log (x)))
    • Для каждого следующего элемента мы проверяем, является ли он больше, чем наименьший элемент в LIST, и, если это, вынимаем наименьший элемент и вставляем текущий элемент в LIST. Поскольку это упорядоченный список, каждый элемент должен найти свое место в логарифмическом времени (бинарный поиск), и поскольку упорядоченный список не является проблемой, вставка не является проблемой. Каждый шаг также выполняется за постоянное время (O (log (x)).

    Итак, каков наихудший сценарий?

    x log (x) + (nx) (log (x) +1) = nlog (x) + n — x

    Так что это O (N) время для худшего случая. +1 — это проверка, если число больше, чем наименьшее число в LIST. Ожидаемое время для среднего случая будет зависеть от математического распределения этих n элементов.

    Возможные улучшения

    Этот алгоритм может быть немного улучшен для наихудшего сценария, но IMHO (я не могу доказать это утверждение), который ухудшит среднее поведение. Асимптотическое поведение будет таким же.

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

    x log (x) + (nx) log (x) = nlog (x)

    Для этого варианта использования я не вижу дальнейших улучшений. И все же вы должны спросить себя — что если мне придется делать это больше, чем log (n) раз и для разных x-es? Очевидно, что мы отсортировали бы этот массив в O (n log (n)) и взяли наш элемент x всякий раз, когда они нам нужны.

    Автор: Rouz Размещён: 25.10.2013 12:49

    1 плюс

    1913 Репутация автора

    На этот вопрос будет дан ответ со сложностью N log (100) (вместо N log N) с одной строкой кода C ++.

    Окончательным ответом будет вектор, в котором первые 100 элементов будут гарантированно равны 100 самым большим числам вашего массива, а остальные элементы не упорядочены.

    C ++ STL (стандартная библиотека) весьма удобен для решения подобных задач.

    Примечание: я не говорю, что это оптимальное решение, но оно спасло бы ваше интервью.

    плюса

    717 Репутация автора

    я сделал свой собственный код, не уверен, что это то, что «интервьюер» это смотрит

    Автор: Javier Размещён: 11.05.2015 09:04

    1 плюс

    41740 Репутация автора

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

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

    Поэтому сначала мы выбираем, скажем, 100 000 случайных чисел из массива. Чтобы избежать случайного доступа, который может быть медленным, мы добавим, скажем, 400 случайных групп по 250 последовательных чисел. При таком случайном выборе мы можем быть совершенно уверены, что очень немногие из оставшихся чисел входят в первую сотню, поэтому время выполнения будет очень близко к времени простого цикла, сравнивающего миллиард чисел с некоторым максимальным значением.

    плюса

    37 Репутация автора

    Возможные улучшения.

    Если файл содержит 1 миллиардное число, чтение может быть очень долгим .

    Чтобы улучшить эту работу, вы можете:

    • Разделите файл на n частей, создайте n потоков, заставьте n потоков искать по 100 самых больших чисел в своей части файла (используя очередь с приоритетами) и, наконец, получить 100 самых больших чисел всех потоков, выведенных.
    • Используйте кластер для выполнения такой задачи с помощью решения, подобного hadoop. Здесь вы можете разделить файл еще больше и получить более быстрый вывод для файла с 1 миллиардом (или 10 ^ 12) чисел.

    Автор: Maxime B. Размещён: 02.08.2020 01:27

    1 плюс

    39 Репутация автора

    Поиск лучших 100 из миллиарда чисел лучше всего сделать, используя минимальную кучу из 100 элементов.

    Сначала запишите мин-кучу с первыми 100 встреченными числами. min-heap будет хранить наименьшее из первых 100 чисел в корне (вверху).

    Теперь, когда вы идете вдоль остальных чисел, сравните их только с корнем (наименьшее из 100).

    Если обнаруженное новое число больше, чем корень из min-heap, замените корень на это число, иначе проигнорируйте его.

    Как часть вставки нового числа в min-heap наименьшее число в куче придет к вершине (root).

    Как только мы пройдем все числа, у нас будут самые большие 100 чисел в минимальной куче.

    Автор: imsaar Размещён: 24.11.2020 10:55

    плюса

    387 Репутация автора

    Сначала возьмите 1000 элементов и добавьте их в максимальную кучу. Теперь возьмите первые 100 элементов и сохраните их где-нибудь. Теперь выберите следующие 900 элементов из файла и добавьте их в кучу вместе с последними 100 самыми старшими элементами.

    Продолжайте повторять этот процесс, выбирая 100 элементов из кучи и добавляя 900 элементов из файла.

    Окончательный выбор из 100 элементов даст нам максимум 100 элементов из миллиарда чисел.

    Мастер Йода рекомендует:  Что делать, если дома не работает интернет
    Добавить комментарий