Хорошим местом для начала является JavaDocs . Они охватывают это:
Брошено, когда приложение пытается использовать null в случае, когда требуется объект. К ним относятся:
- Вызов метода экземпляра нулевого объекта.
- Доступ или изменение поля нулевого объекта.
- Выполнение длины null, как если бы это был массив.
- Доступ или изменение слотов с нулевым значением, как если бы это был массив.
- Бросать нуль, как если бы это было значение Throwable.
Приложения должны бросать экземпляры этого класса для указания других незаконных видов использования нулевого объекта.
blockquote>Также, если вы попытаетесь использовать нулевую ссылку с
synchronized
, который также выдаст это исключение, за JLS :SynchronizedStatement: synchronized ( Expression ) Block
blockquote>
- В противном случае, если значение выражения равно null,
NullPointerException
.Как это исправить?
Итак, у вас есть
NullPointerException
. Как вы это исправите? Возьмем простой пример, который выдаетNullPointerException
:public class Printer { private String name; public void setName(String name) { this.name = name; } public void print() { printString(name); } private void printString(String s) { System.out.println(s + " (" + s.length() + ")"); } public static void main(String[] args) { Printer printer = new Printer(); printer.print(); } }
Идентифицирует нулевые значения
. Первый шаг - точно определить , значения которого вызывают исключение . Для этого нам нужно выполнить некоторую отладку. Важно научиться читать stacktrace . Это покажет вам, где было выбрано исключение:
Exception in thread "main" java.lang.NullPointerException at Printer.printString(Printer.java:13) at Printer.print(Printer.java:9) at Printer.main(Printer.java:19)
Здесь мы видим, что исключение выбрано в строке 13 (в методе
printString
). Посмотрите на строку и проверьте, какие значения равны нулю, добавив протоколирующие операторы или используя отладчик . Мы обнаруживаем, чтоs
имеет значение null, а вызов методаlength
на него вызывает исключение. Мы видим, что программа перестает бросать исключение, когдаs.length()
удаляется из метода.Трассировка, где эти значения взяты из
Затем проверьте, откуда это значение. Следуя вызовам метода, мы видим, что
s
передается сprintString(name)
в методеprint()
, аthis.name
- null.Трассировка, где эти значения должны быть установлены
Где установлен
this.name
? В методеsetName(String)
. С некоторой дополнительной отладкой мы видим, что этот метод вообще не вызывается. Если этот метод был вызван, обязательно проверьте порядок , что эти методы вызывают, а метод set не будет называться после методом печати. Этого достаточно, чтобы дать нам решение: добавить вызов
printer.setName()
перед вызовомprinter.print()
.Другие исправления
Переменная может иметь значение по умолчанию (и
setName
может помешать ему установить значение null):private String name = "";
Либо метод
printString
может проверить значение null например:printString((name == null) ? "" : name);
Или вы можете создать класс, чтобы
name
всегда имел ненулевое значение :public class Printer { private final String name; public Printer(String name) { this.name = Objects.requireNonNull(name); } public void print() { printString(name); } private void printString(String s) { System.out.println(s + " (" + s.length() + ")"); } public static void main(String[] args) { Printer printer = new Printer("123"); printer.print(); } }
См. также:
Я все еще не могу найти проблему
Если вы попытались отладить проблему и до сих пор не имеете решения, вы можете отправить вопрос для получения дополнительной справки, но не забудьте включить то, что вы пробовали до сих пор. Как минимум, включите stacktrace в вопрос и отметьте важные номера строк в коде. Также попробуйте сначала упростить код (см. SSCCE ).
Под числами предположения меньше чем один не позволяется и нет никаких дубликатов, существуют простые идентификационные данные суммирования для этого - сумма чисел от 1
до m
в инкрементах 1
(m * (m + 1)) / 2
. Можно тогда суммировать массив и использовать эти идентификационные данные.
можно узнать, существует ли простофиля под вышеупомянутыми гарантиями плюс гарантия, никакое число не выше m или меньше, чем n (который может быть проверен в O(N)
)
идея в псевдокоде:
0) Запускаются в N = 0
, 1) Берут Энный элемент в списке.
2), Если это не находится в правильном месте, если список был отсортирован, проверьте, где это должно быть.
3), Если место, где это должно быть уже, имеет то же число, у Вас есть простофиля - TRUE
4 ВОЗВРАТА), Иначе, подкачайте числа (для помещения первого числа на правильное место).
5) С числом Вы просто подкачали с, он в правильном месте?
6), Если не, вернитесь к шагу два.
7) Иначе, запустите на шаге один с N = N + 1. Если это прошло бы конец списка, у Вас нет простофиль.
И, да, который работает в O(N)
, хотя это может быть похожим O(N ^ 2)
Это решение работы под предположением, Вы можете изменить массив, затем используете оперативный вид Основания (который достигает O(N)
скорость).
Другие mathy-решения были выдвинуты, но я не уверен, что любой из них был доказан. Существует набор сумм, которые могли бы быть полезными, но большинство из них сталкивается со взрывом в числе битов, требуемых представить сумму, которая нарушит постоянную гарантию дополнительного пространства. Я также не знаю, способен ли какой-либо из них к созданию отличного числа для данного набора чисел. Я думаю, что сумма квадратов могла бы работать, который имеет известную формулу для вычисления ее (см. Вольфрам )
Так, это было упомянуто, чтобы, возможно, использовать сумму + сумма квадратов. Никто не знал, работало ли это или нет, и я понял, что это только становится проблемой когда (x + y) = (n + m), такие как факт 2 + 2 = 1 + 3. Квадраты также имеют эту проблему благодаря [1 111], Пифагореец утраивается (так 3^2 + 4^2 + 25^2 == 5^2 + 7^2 + 24^2, и сумма квадратов не работает). Если мы используем Великая теорема Ферма , мы знаем, что этого не может произойти для n^3. Но мы также не знаем, нет ли никакого x + y + z = n для этого (если мы не делаем и я не знаю это). Так никакая гарантия это также не повреждается - и если мы продолжаем вниз этот путь, у нас быстро заканчиваются биты.
В моем ликовании, однако, я забыл отмечать, что можно повредить сумму квадратов, но при этом Вы создаете нормальную сумму, которая не допустима. Я не думаю, что можно сделать обоих, но, как был отмечен, у нас нет доказательства так или иначе.
<час>я должен сказать, нахождение, что контрпримеры иногда намного легче, чем доказательство вещей! Рассмотрите следующие последовательности, все из которых имеют сумму 28 и сумму квадратов 140:
[1, 2, 3, 4, 5, 6, 7]
[1, 1, 4, 5, 5, 6, 6]
[2, 2, 3, 3, 4, 7, 7]
я не мог найти никакие подобные примеры длины 6 или меньше. Если Вы хотите пример, который имеет надлежащую минуту и макс. значения также, попробуйте эту из длины 8:
[1, 3, 3, 4, 4, 5, 8, 8]
<час> целочисленный массив длины m содержит все числа от n до n+m-1 точно однажды эквивалентности
(Причина: существуют только m значения в данном целочисленном диапазоне, поэтому если массив содержит m уникальные значения в этом диапазоне, это должно содержать каждые из них однажды)
, Если Вам разрешают изменить массив, можно проверить обоих в одну передачу через список с измененной версией идеи алгоритма hazzen (нет никакой потребности сделать любое суммирование):
ВОЗВРАТА, я не уверен, говорит ли модификация исходного массива против максимального предоставленного дополнительного пространства O (1), но если это не делает это должно быть решением исходный требуемый плакат.
ciphwn имеет его правильный. Это - все, чтобы сделать со статистикой. То, что спрашивает вопрос, в статистических терминах, то, действительно ли форма последовательности чисел дискретное равномерное распределение. Дискретное равномерное распределение состоит в том, где все значения конечного множества возможных значений одинаково вероятны. К счастью, существуют некоторые полезные формулы, чтобы определить, универсален ли дискретный набор. Во-первых, для определения среднего из набора (a.. b) (a+b)/2, и различие (n.n-1)/12. Затем, определите различие данного набора:
variance = sum [i=1..n] (f(i)-mean).(f(i)-mean)/n
и затем соответствуют ожидаемому различию. Это потребует два, передает по данным, однажды чтобы определить среднее и снова вычислить различие.
Ссылки:
Массив содержит числа N, и Вы хотите определить, суммируют ли два из чисел к данному номеру K. Например, если вход 8,4, 1,6, и K равняется 10, ответ - да (4 и 6). Число может использоваться дважды. Сделайте следующее. a. Даете O (N2) алгоритм для решения этой проблемы. b. Даете O (N, регистрируют N), алгоритм для решения этой проблемы. (Подсказка: Отсортируйте объекты сначала. После выполнения так, можно решить проблему в линейное время.) c. Кодируйте оба решения и сравните время выполнения Ваших алгоритмов. 4.
Ruby Kent Fredric (для упрощения тестирования)
Контрпример (для версии C): {8, 33, 27, 30, 9, 2, 35, 7, 26, 32, 2, 23, 0, 13, 1, 6, 31, 3, 28, 4, 5, 18, 12, 2, 9, 14, 17, 21, 19, 22, 15, 20, 24, 11, 10, 16, 25}. Здесь n=0, m=35. Эта последовательность промахи 34
и имеет два 2
.
Это - O (m) вовремя и O (1) в решении для пространства.
значения Из диапазона легко обнаруживаются в O (n) вовремя и O (1) в пространстве, поэтому тесты сконцентрированы на в диапазоне (означает, что все значения находятся в допустимом диапазоне [n, n+m)
), последовательности. Иначе {1, 34}
встречный пример (для версии C, sizeof (интервал) == 4, стандартное двоичное представление чисел).
основное различие между C и версией Ruby: <<
оператор повернет значения в C из-за конечного sizeof (интервал), но в Ruby числа вырастут для размещения результата, например,
Ruby: 1 << 100 # -> 1267650600228229401496703205376
C: int n = 100; 1 << n // -> 16
В Ruby: check_index ^= 1 << i;
эквивалентно [1 111]. Тот же эффект мог быть реализован в C++: vector<bool> v(m); v[i] = true;
bool isperm_fredric(int m; int a[m], int m, int n)
{
/**
O(m) in time (single pass), O(1) in space,
no restriction on n,
?overflow?
a[] may be readonly
*/
int check_index = 0;
int check_value = 0;
int min = a[0];
for (int i = 0; i < m; ++i) {
check_index ^= 1 << i;
check_value ^= 1 << (a[i] - n); //
if (a[i] < min)
min = a[i];
}
check_index <<= min - n; // min and n may differ e.g.,
// {1, 1}: min=1, but n may be 0.
return check_index == check_value;
}
Значения вышеупомянутой функции были протестированы против следующего кода:
bool *seen_isperm_trusted = NULL;
bool isperm_trusted(int m; int a[m], int m, int n)
{
/** O(m) in time, O(m) in space */
for (int i = 0; i < m; ++i) // could be memset(s_i_t, 0, m*sizeof(*s_i_t));
seen_isperm_trusted[i] = false;
for (int i = 0; i < m; ++i) {
if (a[i] < n or a[i] >= n + m)
return false; // out of range
if (seen_isperm_trusted[a[i]-n])
return false; // duplicates
else
seen_isperm_trusted[a[i]-n] = true;
}
return true; // a[] is a permutation of the range: [n, n+m)
}
Входные массивы сгенерированы с:
void backtrack(int m; int a[m], int m, int nitems)
{
/** generate all permutations with repetition for the range [0, m) */
if (nitems == m) {
(void)test_array(a, nitems, 0); // {0, 0}, {0, 1}, {1, 0}, {1, 1}
}
else for (int i = 0; i < m; ++i) {
a[nitems] = i;
backtrack(a, m, nitems + 1);
}
}
Ответ от "nickf" dows не работает, если массив не отсортирован var_dump (testArray (массив (5, 3, 1, 2, 4), 1, 5));//дает "дубликаты"!!!!
Также Ваша формула для вычисления суммы ([n... n+m-1]) выглядит неправильной...., корректная формула (m (m+1)/2 - n (n-1)/2)
Почему другие решения используют суммирование каждого значения? Я думаю, что это опасно, потому что, когда Вы добавляете вместе O (n) объекты в одно число, Вы технически используете больше, чем O (1) пространство.
Более простой метод:
Шаг 1, выясните, существуют ли какие-либо дубликаты. Я не уверен, возможно ли это в O (1) пространство. Так или иначе возвратите false, если существуют дубликаты.
Шаг 2, выполните итерации через список, отслеживайте самый низкий и самый высокий объекты.
Шаг 3, Делает (самый высокий - самый низкий), равняются m? Если так, возвратите true.
Алгоритм любой-передачи требует Омеги (n) биты устройства хранения данных.
предположим об обратном, что там существует алгоритм с одной передачей, который использует o (n) биты. Поскольку это делает только одну передачу, это должно суммировать первые значения n/2 в o (n) пространство. С тех пор существуют C (n, n/2) = 2^Theta (n) возможные наборы значений n/2, оттянутых из S = {1..., n}, там существуйте, два отличных набора A и B n/2 оценивают таким образом, что состояние памяти является тем же после обоих. Если' = S \A является "корректным" набором значений к дополнению A, то алгоритм не может возможно ответить правильно за исходные данные
А' - да
B' - никакой
, так как это не может отличить первый случай от второго.
Q.E.D.
Провалите меня, если я неправ, но я думаю, что мы можем определить, существуют ли дубликаты или не использование различия. Поскольку мы знаем среднее заранее (n + (m-1)/2, или что-то как этот) мы можем просто подвести итог чисел и квадрата различия, чтобы означать видеть, соответствует ли сумма уравнению (млн + m (m-1)/2), и различие (0 + 1 + 4 +... + (m-1) ^2)/m. Если различие не соответствует, вероятно, что у нас есть дубликат.
РЕДАКТИРОВАНИЕ: различие, как предполагается, (0 + 1 + 4 +... + [(m-1)/2] ^2) *2/m, потому что половина элементов является меньше, чем среднее и другая половина больше, чем среднее.
, Если существует дубликат, термин на вышеупомянутом уравнении будет отличаться от корректной последовательности, даже если другой дубликат полностью уравновесит изменение в среднем. Таким образом, функция возвращает true, только если и сумма и различие соответствуют значениям desrired, которые мы можем вычислить заранее.
, Это использует псевдокод, предложенный Hazzen плюс некоторые мои собственные идеи. Это работает на отрицательные числа также и не требует никакого материала sum-of-the-squares.
function testArray($nums, $n, $m) {
// check the sum. PHP offers this array_sum() method, but it's
// trivial to write your own. O(n) here.
if (array_sum($nums) != ($m * ($m + 2 * $n - 1) / 2)) {
return false; // checksum failed.
}
for ($i = 0; $i < $m; ++$i) {
// check if the number is in the proper range
if ($nums[$i] < $n || $nums[$i] >= $n + $m) {
return false; // value out of range.
}
while (($shouldBe = $nums[$i] - $n) != $i) {
if ($nums[$shouldBe] == $nums[$i]) {
return false; // duplicate
}
$temp = $nums[$i];
$nums[$i] = $nums[$shouldBe];
$nums[$shouldBe] = $temp;
}
}
return true; // huzzah!
}
var_dump(testArray(array(1, 2, 3, 4, 5), 1, 5)); // true
var_dump(testArray(array(5, 4, 3, 2, 1), 1, 5)); // true
var_dump(testArray(array(6, 4, 3, 2, 0), 1, 5)); // false - out of range
var_dump(testArray(array(5, 5, 3, 2, 1), 1, 5)); // false - checksum fail
var_dump(testArray(array(5, 4, 3, 2, 5), 1, 5)); // false - dupe
var_dump(testArray(array(-2, -1, 0, 1, 2), -2, 5)); // true
Некоторое время назад я слышал об очень умном алгоритме сортировки от кого-то, кто работал на телефонную компанию. Они должны были отсортировать крупное количество номеров телефона. После прохождения через набора различных стратегий вида они наконец совершают нападки на очень изящном решении: они просто создали немного массива и рассматривали смещение в битовый массив как номер телефона. Они тогда развернулись через их базу данных с единственной передачей, изменив бит для каждого числа к 1. После этого они развернулись через битовый массив однажды, выложив номера телефона для записей, которые имели набор битов высоко.
Вдоль тех строк, я полагаю, что можно использовать данные в самом массиве как структура метаданных для поиска дубликатов. Худший случай, у Вас мог быть отдельный массив, но я вполне уверен, можно использовать входной массив, если Вы не возражаете против небольшого количества свопинга.
я собираюсь не учесть n параметр в течение времени, будучи, b/c, который просто путает вещи - добавляющий в индексном смещении, довольно легко сделать.
Рассмотрите:
for i = 0 to m
if (a[a[i]]==a[i]) return false; // we have a duplicate
while (a[a[i]] > a[i]) swapArrayIndexes(a[i], i)
sum = sum + a[i]
next
if sum = (n+m-1)*m return true else return false
Это не O (n) - вероятно, ближе к O (n Журнал n) - но это действительно предусматривает постоянное пространство и может обеспечить различный вектор нападения для проблемы.
, Если мы хотим O (n), затем с помощью массива байтов и некоторых битовых операций предоставит проверке дублирования дополнительные n/32 байты используемой памяти (принятие 32 битов ints, конечно).
РЕДАКТИРОВАНИЕ: вышеупомянутый алгоритм мог быть улучшен далее путем добавления, что сумма проверяет к внутренней части цикла и проверяет на:
if sum > (n+m-1)*m return false
тот путь это перестанет работать быстро.
Принятие Вас знает только длину массива, и Вам разрешают изменить массив, это может быть сделано в O (1) пространство и O (n) время.
процесс имеет два простых шага. 1. "вид по модулю" массив. [5,3,2,4] => [4,5,2,3] (O (2n)) 2. Проверьте, что сосед каждого значения один выше, чем себя (по модулю) (O (n))
, Все сказали, что Вам нужно самое большее 3, проходит через массив.
вид по модулю является 'хитрой' частью, но цель проста. Примите каждое значение в массиве и сохраните его в его собственном адресе (длина по модулю). Это требует одной передачи через массив, цикличное выполнение по каждому местоположению, 'выселяющему' его значение путем свопинга его к его корректному местоположению и перемещения в значение в его месте назначения. Если Вы когда-нибудь перемещаетесь в значение, которое является конгруэнтным значению, которое Вы просто выселили, Вы имеете дубликат и можете выйти рано. Худший случай, это - O (2n).
проверка является единственной передачей через массив, исследующий каждое значение с, он - затем самый высокий сосед. Всегда O (n).
Объединенный алгоритм является O (n) +O (2n) = O (3n) = O (n)
Псевдокод из моего решения:
foreach(values[]) while(values[i] not congruent to i) to-be-evicted = values[i] evict(values[i]) // swap to its 'proper' location if(values[i]%length == to-be-evicted%length) return false; // a 'duplicate' arrived when we evicted that number end while end foreach foreach(values[]) if((values[i]+1)%length != values[i+1]%length) return false end foreach
я включал подтверждение концепции кода Java ниже, это не симпатично, но это передает все модульные тесты, которые я сделал для него. Я называю их 'StraightArray', потому что они соответствуют покерной комбинации прямого (непрерывный костюм для игнорирования последовательности).
public class StraightArray {
static int evict(int[] a, int i) {
int t = a[i];
a[i] = a[t%a.length];
a[t%a.length] = t;
return t;
}
static boolean isStraight(int[] values) {
for(int i = 0; i < values.length; i++) {
while(values[i]%values.length != i) {
int evicted = evict(values, i);
if(evicted%values.length == values[i]%values.length) {
return false;
}
}
}
for(int i = 0; i < values.length-1; i++) {
int n = (values[i]%values.length)+1;
int m = values[(i+1)]%values.length;
if(n != m) {
return false;
}
}
return true;
}
}
МОЙ ТЕКУЩИЙ НАИЛУЧШИЙ ВАРИАНТ
def uniqueSet( array )
check_index = 0;
check_value = 0;
min = array[0];
array.each_with_index{ |value,index|
check_index = check_index ^ ( 1 << index );
check_value = check_value ^ ( 1 << value );
min = value if value < min
}
check_index = check_index << min;
return check_index == check_value;
end
O (n) и Пространство O (1)
я записал сценарий в комбинации грубой силы, которые могли привести это к сбою, и это не нашло никого. Если у Вас есть массив, который нарушает эту функцию, действительно говорят.:)
<час>@J.F. Sebastian
не истинный алгоритм хеширования. Технически, это - высокоэффективный упакованный булев массив "замеченных" значений.
ci = 0, cv = 0
[5,4,3]{
i = 0
v = 5
1 << 0 == 000001
1 << 5 == 100000
0 ^ 000001 = 000001
0 ^ 100000 = 100000
i = 1
v = 4
1 << 1 == 000010
1 << 4 == 010000
000001 ^ 000010 = 000011
100000 ^ 010000 = 110000
i = 2
v = 3
1 << 2 == 000100
1 << 3 == 001000
000011 ^ 000100 = 000111
110000 ^ 001000 = 111000
}
min = 3
000111 << 3 == 111000
111000 === 111000
точка этого являющегося главным образом, что для "фальсифицирования" больше всего проблемы случается, каждый использует дубликаты, чтобы сделать так. В этой системе XOR штрафует Вас за использование того же значения дважды и предполагает, что Вы вместо этого сделали это 0 раз.
протесты, здесь являющиеся, конечно:
$x
в ( 1 << $x > 0 )
, окончательная эффективность зависит от того, как Ваша базовая система реализует способности к:
редактирование Отмеченный, выше операторов кажутся сбивающими с толку. Принятие идеальной машины, где "целое число" является регистром с точностью Бога, которая может все еще выполнить ^ b в O (1) время.
, Но приводящий эти предположения к сбою, нужно начать спрашивать алгоритмическую сложность простой математики.
Путем работы с a[i] % a.length
вместо a[i]
Вы уменьшаете проблему до необходимости решить, что у Вас есть числа 0
к a.length - 1
.
Мы считаем само собой разумеющимся это наблюдение и попытку проверить, содержит ли массив [0, m).
Находят первый узел, это не находится в его правильном положении, например,
0 1 2 3 7 5 6 8 4 ; the original dataset (after the renaming we discussed)
^
`---this is position 4 and the 7 shouldn't be here
Подкачка, что число в то, где это должно быть. т.е. подкачайте 7
с 8
:
0 1 2 3 8 5 6 7 4 ;
| `--------- 7 is in the right place.
`--------------- this is now the 'current' position
Теперь мы повторяем это. При рассмотрении снова нашей текущей позиции мы спрашиваем:
"действительно ли это - корректное число для здесь?"
После этого правила снова, мы добираемся:
0 1 2 3 4 5 6 7 8 ; 4 and 8 were just swapped
Это будет постепенно создавать список правильно слева направо, и каждое число будет перемещено самое большее однажды, и следовательно это - O (n).
, Если существуют простофили, мы заметим его, как скоро существует попытка подкачать номер backwards
в списке.
Если Вы хотите знать сумму чисел [n ... n + m - 1]
просто использование это уравнение.
var sum = m * (m + 2 * n - 1) / 2;
, Который работает на любое число, положительное или отрицательное, даже если n является десятичным числом.
, Почему другие решения используют суммирование каждого значения? Я думаю, что это опасно, потому что, когда Вы добавляете вместе O (n) объекты в одно число, Вы технически используете больше, чем O (1) пространство.
O (1) указывает на постоянное пространство, которое не изменяется количеством n. Не имеет значения, если это - 1 или 2 переменные, пока это - постоянное число. Почему Вы говорите, что это - больше, чем O (1) пространство? При вычислении суммы n чисел путем накопления его во временной переменной Вы использовали бы точно 1 переменную так или иначе.
Комментарий в ответе, потому что система не позволяет мне писать комментарии все же.
Обновление (в ответ на комментарии): в этом ответе я имел в виду O (1) пространство везде, где "пространство" или "время" было опущено. Заключенный в кавычки текст является частью более раннего ответа, к которому это - ответ на.
Учитывая это -
Запись метод, который берет международный массив из размера m...
я предполагаю, что справедливо прийти к заключению, что существует верхний предел для m, равного значению самого большого интервал (2^32 быть типичным). , Другими словами, даже при том, что m не определяется как интервал, то, что массив не может иметь дубликатов, подразумевает, что не может быть больше, чем количество значений, которые можно сформировать из 32 битов, который в свою очередь подразумевает, что m ограничен, чтобы быть интервалом также.
, Если такое заключение приемлемо, то я предлагаю использовать фиксированное пространство (2^33 + 2) * 4 байта = 34,359,738,376 байтов = 34.4 ГБ для обработки всех возможных случаев. (Не подсчет пространства, требуемого входным массивом и его циклом).
, Конечно, для оптимизации, я сначала принял бы m во внимание и выделил бы только фактическую необходимую сумму, (2m+2) * 4 байта.
, Если это приемлемо для O (1), пространственное ограничение - для установленной проблемы - тогда позволило мне продолжить двигаться к алгоритмическому предложению...:)
Предположения : массив m ints, положительный или отрицательный, ни одно большее, чем, что могут содержать 4 байта. Дубликаты обрабатываются. Первое значение может быть любым допустимым интервалом, Ограничивают m как выше.
Первый , создайте международный массив длины 2m-1, ary, и обеспечьте три международных переменные: уехал , разность , и право . Заметьте, что это делает 2m+2...
115-секундный , примите первое значение от входного массива и скопируйте его в положение m-1 в новом массиве. Инициализируйте эти три переменные.
Треть , цикл через остающиеся значения во входном массиве, и сделайте следующее для каждого повторения:
, я решил поместить это в код, и это работало.
Вот рабочий образец с помощью C#:
public class Program
{
static bool puzzle(int[] inAry)
{
var m = inAry.Count();
var outAry = new int?[2 * m - 1];
int diff = 0;
int left = 0;
int right = 0;
outAry[m - 1] = inAry[0];
for (var i = 1; i < m; i += 1)
{
diff = inAry[i] - inAry[0];
if (diff > m - 1 + right || diff < 1 - m + left) return false;
if (outAry[m - 1 + diff] != null) return false;
outAry[m - 1 + diff] = inAry[i];
if (diff > left) left = diff;
if (diff < right) right = diff;
}
return true;
}
static void Main(string[] args)
{
var inAry = new int[3]{ 2, 3, 4 };
Console.WriteLine(puzzle(inAry));
inAry = new int[13] { -3, 5, -1, -2, 9, 8, 2, 3, 0, 6, 4, 7, 1 };
Console.WriteLine(puzzle(inAry));
inAry = new int[3] { 21, 31, 41 };
Console.WriteLine(puzzle(inAry));
Console.ReadLine();
}
}
примечание : этот комментарий основан на оригинальном тексте вопроса (это было исправлено, с тех пор)
, Если вопрос поставлен точно , как записано выше (и это не просто опечатка) и для массива размера n функция должен возвратиться (Истинный/Ложный), если массив будет состоять из чисел 1... n+1,
... тогда, то ответ всегда будет ложью, потому что массив со всеми числами 1... n+1 будет иметь размер n+1 и не n. следовательно, на вопрос можно ответить в O (1).:)
(не может отправить его как комментарий)
@popopome
Для a = {0, 2, 7, 5,}
это возвращается true
(означает, что a
перестановка диапазона [0, 4)
), но это должно возвратиться false
в этом случае (a
, очевидно, не перестановка [0, 4)
).
Другой встречный пример: {0, 0, 1, 3, 5, 6, 6}
- все значения находятся в диапазоне, но существуют дубликаты.
я мог неправильно реализовать идею popopome (или тесты), поэтому вот код:
bool isperm_popopome(int m; int a[m], int m, int n)
{
/** O(m) in time (single pass), O(1) in space,
no restrictions on n,
no overflow,
a[] may be readonly
*/
int even_xor = 0;
int odd_xor = 0;
for (int i = 0; i < m; ++i)
{
if (a[i] % 2 == 0) // is even
even_xor ^= a[i];
else
odd_xor ^= a[i];
const int b = i + n;
if (b % 2 == 0) // is even
even_xor ^= b;
else
odd_xor ^= b;
}
return (even_xor == 0) && (odd_xor == 0);
}
(для предотвращения неверного истолкования псевдокода)
Встречный пример: {1, 1, 2, 4, 6, 7, 7}
.
int pow_minus_one(int power)
{
return (power % 2 == 0) ? 1 : -1;
}
int ceil_half(int n)
{
return n / 2 + (n % 2);
}
bool isperm_b3_3(int m; int a[m], int m, int n)
{
/**
O(m) in time (single pass), O(1) in space,
doesn't use n
possible overflow in sum
a[] may be readonly
*/
int altsum = 0;
int mina = INT_MAX;
int maxa = INT_MIN;
for (int i = 0; i < m; ++i)
{
const int v = a[i] - n + 1; // [n, n+m-1] -> [1, m] to deal with n=0
if (mina > v)
mina = v;
if (maxa < v)
maxa = v;
altsum += pow_minus_one(v) * v;
}
return ((maxa-mina == m-1)
and ((pow_minus_one(mina + m-1) * ceil_half(mina + m-1)
- pow_minus_one(mina-1) * ceil_half(mina-1)) == altsum));
}
В Python:
def ispermutation(iterable, m, n):
"""Whether iterable and the range [n, n+m) have the same elements.
pre-condition: there are no duplicates in the iterable
"""
for i, elem in enumerate(iterable):
if not n <= elem < n+m:
return False
return i == m-1
print(ispermutation([1, 42], 2, 1) == False)
print(ispermutation(range(10), 10, 0) == True)
print(ispermutation((2, 1, 3), 3, 1) == True)
print(ispermutation((2, 1, 3), 3, 0) == False)
print(ispermutation((2, 1, 3), 4, 1) == False)
print(ispermutation((2, 1, 3), 2, 1) == False)
Это - O (m) вовремя и O (1) в пространстве. Это делает не , принимают во внимание дубликаты.
Альтернативное решение:
def ispermutation(iterable, m, n):
"""Same as above.
pre-condition: assert(len(list(iterable)) == m)
"""
return all(n <= elem < n+m for elem in iterable)
def test(a, n, m):
seen = [False] * m
for x in a:
if x < n or x >= n+m:
return False
if seen[x-n]:
return False
seen[x-n] = True
return False not in seen
print test([2, 3, 1], 1, 3)
print test([1, 3, 1], 1, 3)
print test([1, 2, 4], 1, 3)
Примечание, что это только делает одну передачу через первый массив, не считая линейный поиск вовлеченным в not in
.:)
я также, возможно, использовал python set
, но я выбрал простое решение, где рабочие характеристики set
нельзя рассмотреть.
Обновление: Smashery указал, что у меня был misparsed "постоянный объем памяти", и это решение на самом деле не решает проблему.
so in one pass you can compute the product of the m numbers, also compute m! and see if the product modulo m ! is zero at the end of the pass
I might be missing something but this is what comes to my mind ...
something like this in python
my_list1 = [9,5,8,7,6]
my_list2 = [3,5,4,7]
def consecutive(my_list):
count = 0
prod = fact = 1
for num in my_list:
prod *= num
count +=1
fact *= count
if not prod % fact:
return 1
else:
return 0
print consecutive(my_list1)
print consecutive(my_list2)
HotPotato ~$ python m_consecutive.py
1
0
Я предлагаю следующее:
Выберите конечный набор простых чисел P_1, P_2, ..., P_K и вычислить вхождения элементов во входной последовательности (минус минимум) по модулю каждого P_i. Образец допустимой последовательности известен.
Например, для последовательности из 17 элементов по модулю 2 у нас должен быть профиль: [9 8], по модулю 3: [6 6 5], по модулю 5: [4 4 3 3 3] и др.
Комбинируя тест с использованием нескольких баз, мы получаем все более точный вероятностный тест. Поскольку элементы ограничены целым размером, существует конечная база, обеспечивающая точный тест. Это похоже на вероятностные тесты псевдопростоты.
S_i is an int array of size P_i, initially filled with 0, i=1..K
M is the length of the input sequence
Mn = INT_MAX
Mx = INT_MIN
for x in the input sequence:
for i in 1..K: S_i[x % P_i]++ // count occurrences mod Pi
Mn = min(Mn,x) // update min
Mx = max(Mx,x) // and max
if Mx-Mn != M-1: return False // Check bounds
for i in 1..K:
// Check profile mod P_i
Q = M / P_i
R = M % P_i
Check S_i[(Mn+j) % P_i] is Q+1 for j=0..R-1 and Q for j=R..P_i-1
if this test fails, return False
return True
Любой непрерывный массив [n, n + 1, ..., n + m-1] может быть отображен на «базовый» интервал [0, 1, ..., m ] с помощью оператора по модулю. Для каждого i в интервале существует ровно один i% m в базовом интервале и наоборот.
Любой непрерывный массив также имеет «интервал» m (максимум - минимум + 1), равный его размеру.
Используя эти факты, вы можете создать "обнаруженный" логический массив того же размера, содержащий изначально все ложные данные, и при посещении входного массива установить для связанных "обнаруженных" элементов значение true.
Этот алгоритм составляет O (n) в пространстве , O (n) вовремя и проверяет наличие дубликатов.
def contiguous( values )
#initialization
encountered = Array.new( values.size, false )
min, max = nil, nil
visited = 0
values.each do |v|
index = v % encountered.size
if( encountered[ index ] )
return "duplicates";
end
encountered[ index ] = true
min = v if min == nil or v < min
max = v if max == nil or v > max
visited += 1
end
if ( max - min + 1 != values.size ) or visited != values.size
return "hole"
else
return "contiguous"
end
end
tests = [
[ false, [ 2,4,5,6 ] ],
[ false, [ 10,11,13,14 ] ] ,
[ true , [ 20,21,22,23 ] ] ,
[ true , [ 19,20,21,22,23 ] ] ,
[ true , [ 20,21,22,23,24 ] ] ,
[ false, [ 20,21,22,23,24+5 ] ] ,
[ false, [ 2,2,3,4,5 ] ]
]
tests.each do |t|
result = contiguous( t[1] )
if( t[0] != ( result == "contiguous" ) )
puts "Failed Test : " + t[1].to_s + " returned " + result
end
end
Мне нравится идея Грега Хьюджилла о сортировке по Radix. Чтобы найти дубликаты, вы можете выполнить сортировку за время O (N) с учетом ограничений на значения в этом массиве.
Для свободного пространства O (1) O (N) времени, которое восстанавливает исходный порядок в списке, вам не нужно делать фактическую замену этого числа; вы можете просто пометить его флагом:
//Java: assumes all numbers in arr > 1
boolean checkArrayConsecutiveRange(int[] arr) {
// find min/max
int min = arr[0]; int max = arr[0]
for (int i=1; i<arr.length; i++) {
min = (arr[i] < min ? arr[i] : min);
max = (arr[i] > max ? arr[i] : max);
}
if (max-min != arr.length) return false;
// flag and check
boolean ret = true;
for (int i=0; i<arr.length; i++) {
int targetI = Math.abs(arr[i])-min;
if (arr[targetI] < 0) {
ret = false;
break;
}
arr[targetI] = -arr[targetI];
}
for (int i=0; i<arr.length; i++) {
arr[i] = Math.abs(arr[i]);
}
return ret;
}
Хранение флагов внутри данного массива является своего рода обманом и не работает с распараллеливанием. Я все еще пытаюсь придумать способ сделать это, не касаясь массива за время O (N) и пространство O (log N). Проверка по сумме и по сумме наименьших квадратов (arr [i] - arr.length / 2.0) ^ 2 кажется, что это может сработать. Одна определяющая характеристика, которую мы знаем о массиве 0 ... m без дубликатов, заключается в том, что он равномерно распределен; мы должны просто проверить это.
Если бы я только мог это доказать.
Я хотел бы отметить, что вышеприведенное решение с использованием факториала занимает O (N) пространства для хранения самого факториала. N! > 2 ^ N, для хранения которого требуется N байтов.
Ой! Я столкнулся с повторяющимся вопросом и не увидел здесь уже идентичных решений. И я подумал, что наконец-то сделал что-то оригинальное! Вот исторический архив того времени, когда я был немного более доволен:
Я не уверен, удовлетворяет ли этот алгоритм всем условиям. Фактически, я даже не подтвердил, что он работает за пределами пары тестовых примеров, которые я пробовал. Даже если у моего алгоритма есть проблемы, я надеюсь, что мой подход приведет к некоторым решениям.
Насколько мне известно, этот алгоритм работает в постоянной памяти и трижды сканирует массив. Возможно, дополнительным бонусом является то, что он работает для всего диапазона целых чисел, если это не было частью исходной проблемы.
Я не очень разбираюсь в псевдокоде, и я действительно думаю, что код может иметь больше смысла, чем слова. Вот реализация, которую я написал на PHP. Прислушайтесь к комментариям.
function is_permutation($ints) {
/* Gather some meta-data. These scans can
be done simultaneously */
$lowest = min($ints);
$length = count($ints);
$max_index = $length - 1;
$sort_run_count = 0;
/* I do not have any proof that running this sort twice
will always completely sort the array (of course only
intentionally happening if the array is a permutation) */
while ($sort_run_count < 2) {
for ($i = 0; $i < $length; ++$i) {
$dest_index = $ints[$i] - $lowest;
if ($i == $dest_index) {
continue;
}
if ($dest_index > $max_index) {
return false;
}
if ($ints[$i] == $ints[$dest_index]) {
return false;
}
$temp = $ints[$dest_index];
$ints[$dest_index] = $ints[$i];
$ints[$i] = $temp;
}
++$sort_run_count;
}
return true;
}
Итак, есть алгоритм, который берет O (n ^ 2), не требует изменения входного массива и занимает постоянное пространство.
Во-первых, предположим, что вы знаете n
и m
. Это линейная операция, поэтому она не добавляет дополнительной сложности. Затем предположим, что существует один элемент, равный n
, и один элемент, равный n + m-1
, а все остальные находятся в [n, n + m)
. Учитывая это, мы можем свести проблему к наличию массива с элементами в [0, m)
.
Теперь, поскольку мы знаем, что элементы ограничены размером массива, мы можем рассматривать каждый элемент как узел с единственной ссылкой на другой элемент; другими словами, массив описывает ориентированный граф. В этом ориентированном графе, если нет повторяющихся элементов, каждый узел принадлежит циклу, то есть узел доступен от самого себя за m
или меньше шагов. Если есть повторяющийся элемент, значит, существует один узел, который вообще недоступен для себя.
Итак, чтобы обнаружить это, вы проходите весь массив от начала до конца и определяете, возвращается ли каждый элемент к себе за <= m
шагов. Если какой-либо элемент недостижим в шагах <= m
, значит, у вас есть дубликат, и вы можете вернуть false. В противном случае, когда вы закончите посещение всех элементов, вы можете вернуть true:
for (int start_index= 0; start_index<m; ++start_index)
{
int steps= 1;
int current_element_index= arr[start_index];
while (steps<m+1 && current_element_index!=start_index)
{
current_element_index= arr[current_element_index];
++steps;
}
if (steps>m)
{
return false;
}
}
return true;
Вы можете оптимизировать это, сохранив дополнительную информацию:
sum_of_steps
. m-sum_of_steps
узлов. Если вы не вернетесь к начальному элементу и не посетите элемент перед начальным элементом, вы нашли цикл, содержащий повторяющиеся элементы, и можете вернуть false
. Это все еще O (n ^ 2), например {1, 2, 3, 0, 5, 6, 7, 4}
, но это немного быстрее.
#include<stdio.h>
#define swapxor(a,i,j) a[i]^=a[j];a[j]^=a[i];a[i]^=a[j];
int check_ntom(int a[], int n, int m) {
int i = 0, j = 0;
for(i = 0; i < m; i++) {
if(a[i] < n || a[i] >= n+m) return 0; //invalid entry
j = a[i] - n;
while(j != i) {
if(a[i]==a[j]) return -1; //bucket already occupied. Dupe.
swapxor(a, i, j); //faster bitwise swap
j = a[i] - n;
if(a[i]>=n+m) return 0; //[NEW] invalid entry
}
}
return 200; //OK
}
int main() {
int n=5, m=5;
int a[] = {6, 5, 7, 9, 8};
int r = check_ntom(a, n, m);
printf("%d", r);
return 0;
}
Редактировать: изменение, внесенное в код для устранения несанкционированного доступа к памяти.
Fail := False;
Sum1 := 0;
Sum2 := 0;
TSum1 := 0;
TSum2 := 0;
For i := 1 to m do
Begin
TSum1 := TSum1 + i;
TSum2 := TSum2 + i * i;
Item := Array[i] - n;
If (Item < 0) or (Item >= m) then
Fail := True
Else
Begin
Sum1 := Sum1 + Item;
Sum2 := Sum2 + Item * Item;
End;
End;
Fail := Fail Or (Sum1 <> TSum1) or (Sum2 <> TSum2);
Усталый и никакой компилятор, но я думаю, что это дает O (m) время выполнения и не может дурачиться.
Кажется, что мы могли проверить на дубликаты путем умножения всех чисел n... n+m вместе, и затем сравнения того значения с ожидаемым продуктом последовательности без дубликатов м! / (n-1)! (отмечают, что это принимает, для последовательности невозможно пройти и ожидаемый тест суммы и ожидаемый тест продукта).
Настолько добавляющий к псевдокод hazzen , мы имеем:
is_range(int[] nums, int n, int m) {
sum_to_m := (m * (m + 1)) / 2
expected_sum := sum_to_m - (n * (n - 1)) / 2
real_sum := sum(nums)
expected_product := m! / (n - 1)!
real_product := product(nums)
return ((real_sum == expected_sum) && (expected_product == real_product))
РЕДАКТИРОВАНИЕ: Вот мое решение в Java с помощью Суммы квадратов для проверки на дубликаты. Это также обрабатывает любой диапазон (включая отрицательные числа) путем смещения последовательности для запуска по телефону 1.
// low must be less than high
public boolean isSequence(int[] nums, int low, int high) {
int shift = 1 - low;
low += shift;
high += shift;
int sum = 0;
int sumSquares = 0;
for (int i = 0; i < nums.length; i++) {
int num = nums[i] + shift;
if (num < low)
return false;
else if (num > high)
return false;
sum += num;
sumSquares += num * num;
}
int expectedSum = (high * (high + 1)) / 2;
if (sum != expectedSum)
return false;
int expectedSumSquares = high * (high + 1) * (2 * high + 1) / 6;
if (sumSquares != expectedSumSquares)
return false;
return true;
}