Ответы пользователя по тегу Алгоритмы
  • Как найти баланс чисел в массиве?

    dollar
    @dollar
    Делай добро и бросай его в воду.
    Алгоритм такой. Все элементы помещаем в одну кучу (массив). Сортируем по убыванию. Дальше начинаем раскидывать по двум новым массивам. Параллельно считаем сумму каждого массива. Таким образом, раскладываем сначала большие числа, потом всё меньше и меньше. Каждый раз кладём новое число в тот массив, где сумма меньше.

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


    UPD:
    Такой алгоритм полного перебора
    function balance(arr1, arr2) {
      let all = arr1.concat(arr2);
    	//all.sort((a, b) => a - b); //Для исключения одинаковых.
    	let all_sum = all.reduce((a,b)=>a+b,0);
    	let len = all.length;
    	let cnt = Math.floor(len * 0.5);
    	let arr_result = new Array(cnt); //Массив выбранных индексов
    	let idx_begin = 0; //Начальная глубина перебора (индекс в arr_result)
    	let sum_begin = 0; //Начальная сумма частично перебранных элементов
    	
    	if (cnt === len * 0.5) { //Оптимизация
    		arr_result[0] = 0;
    		idx_begin = 1;
    		sum_begin = all[0];
    	}
    	
    	let min_diff = all_sum; //Присваиваем какое-то заведомо большое число.
    	let arr_answer; //Итоговый ответ
    	
    	//Проверяем следующий уровень глубины
    	//idx - глубина, sum - сумма всех элементов до этого
    	function check(idx, sum) { 
    		if (idx === cnt) { //Конец перебора. Проверяем, подходит ли.
    			let diff = Math.abs((all_sum - sum) - sum);
    			if (diff < min_diff) { //Подходит
    				min_diff = diff; //Запоминаем новый лучший результат.
    				arr_answer = arr_result.slice(); //Копируем
    			}
    			return;
    		}
    		//Иначе идем дальше вглубь на следующий уровень.
    		let start = idx === 0 ? 0 : arr_result[idx-1] + 1;
    		let max = len - cnt + idx;
    		for(let i = start; i <= max; i++){ //Ключевой цикл алгоритма
    			//if (i > start && all[i] === all[i-1]) continue;
    			arr_result[idx] = i;
    			check(idx+1, sum+all[i]); //Рекурсия
    		}
    	}
    	check(idx_begin,sum_begin); //Начать перебор. Поехали!
    	
    	arr1 = [];
    	arr2 = [];
    	
    	//Фасуем полученный ответ по массивам уже в виде значений.
    	let j = 0;
    	all.forEach((e,i)=>{
    		if (i === arr_answer[j]) {
    			arr1.push(e);
    			j++;
    		} else arr2.push(e);
    	});
    	
    	return {
    		arr1: arr1,
    		arr2: arr2,
    		sum1: arr1.reduce((a,b)=>a+b,0),
    		sum2: arr2.reduce((a,b)=>a+b,0),
    	}
    }
    
    var arr1 = [10, 300, 25, 75];
    var arr2 = [50, 125, 500, 10];
    balance(arr1, arr2);
    Ответ написан
  • Какую модель машинного обучения лучше использовать для прогнозирования движения рейсового транспорта?

    dollar
    @dollar
    Делай добро и бросай его в воду.
    Мне видится, что у вас мало данных для точного прогнозирования.

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

    Приложения, которые нынче занимаются прогнозированием расписания, когда какой транспорт приедет, имеют в распоряжении гораздо больше информации:
    1. Информацию о пробках.
    2. Информацию о GPS координатах каждого транспорта.

    Конечно, точность всё равно не 100%. И порой случаются накладки. Например, мимо остановки может проехать фантомный автобус, или, наоборот, может приехать автобус, которого не существует по данным приложения. Но всё равно точность прогнозирования гораздо выше, чем если основываться лишь на ретроспективном анализе.
    Ответ написан
  • Почему скорость алгоритма измеряется в количестве операций, а не в секундах?

    dollar
    @dollar
    Делай добро и бросай его в воду.
    Потому что скорость выполнения зависит от частоты и типа процессора, а в наше время и от количества ядер/процессоров (потоков).
    Ответ написан
    Комментировать
  • Как оптимальнее реализовать поиск значений в json?

    dollar
    @dollar
    Делай добро и бросай его в воду.
    Через indexOf можно. Это самое оптимальное, что приходит в голову, т.е. самый прямой поиск "в лоб" без лишних вычислений. Оптимальнее только поиск без поиска, когда заранее известны индексы, откуда нужно брать значения.

    var json_str = `,{"name":"Adhi Kot","id":"379","nametype":"Valid","recclass":"EH4","mass":"4239","fall":"Fell","year":"1919-01-01T00:00:00.000","reclat":"32.100000","reclong":"71.800000","geolocation":{"type":"Point","coordinates":[71.8,32.1]}},{"name":"Adzhi-Bogdo (stone)","id":"390","nametype":"Valid","recclass":"LL3-6","mass":"910","fall":"Fell","year":"1949-01-01T00:00:00.000","reclat":"44.833330","reclong":"95.166670","geolocation":{"type":"Point","coordinates":[95.16667,44.83333]}}`;
    var i = 0;
    while((i=json_str.indexOf('"recclass":"',i))!==-1) {
    	i+=12;
    	let j = json_str.indexOf('"',i);
    	let val = json_str.substring(i,j);
    	console.log(val); //выводим очередное значение
    }
    Ответ написан
  • Чем плоха вставка в массив в заданную позицию?

    dollar
    @dollar
    Делай добро и бросай его в воду.
    Прелесть массива в том, что он позволяет искать по индексам. И делается это очень быстро, потому что это индексация в самой памяти. То есть вычисляется адрес ячейки памяти с помощью арифметических операций:
    БАЗА + [РАЗМЕР ЯЧЕЙКИ] * СМЕЩЕНИЕ
    И затем просто происходит обращение на чтение/запись.

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

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

    А если это не возможно, то может быть, что обращение по индексу не нужно, а только полный перебор (в заданном порядке), тогда можно использовать список, где вставка происходит очень быстро. Ну и так далее, зависит от конкретной задачи. Универсальность и скорость алгоритма, как правило, противоречат друг другу.
    Ответ написан
    5 комментариев
  • Проскакивает контроль прыжка ГГ в сторону, по нажатию двух клавиш. Как переделать управление ГГ?

    dollar
    @dollar
    Делай добро и бросай его в воду.
    Вариант 1 (тупой)
    Добавить задержку в 200 мс перед тем, как герой начнет двигаться от клавиш вправо-влево. Это делается просто. В момент нажатия запоминаем текущее время с точностью до миллисекунд.
    А условие движения такое: клавиша вправо зажата И время зажатия больше 200 мс

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

    Вариант 3 (сложный, для комбо)
    Если у вас одновременное нажатие вправо+вверх - это какое-то особое комбо, то можно аналогично первому варианту отслеживать время нажатия вправо и в пределах этой задержки разрешать пользователю совершать данное комбо.
    То есть при нажатии клавиши вправо нужно запомнить текущее время.
    А при нажатии клавиши вверх проверяется условие.
    Условие такое: клавиша вверх нажата И клавиша вправо была нажата менее 200 мс назад

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

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

    Вариант 6 (домашнее задание)
    В случае, если герой в прыжке может менять направление ускорения стрелками, то нужно подойти к геймдизайнеру и спросить, как быть в этому случае. Ведь в конечном итоге всё зависит от его задумки, какие ощущения у игрока он хотел вызвать.
    Ответ написан
    Комментировать
  • Двумерный массив в виде шахматной доски?

    dollar
    @dollar
    Делай добро и бросай его в воду.
    if((Math.floor(i*0.25)+Math.floor(j*0.25))%2 == 0) mas[i][j] +=1;
    Ответ написан
    1 комментарий
  • Как оптимизировать выборку с одновременной сортировкой?

    dollar
    @dollar Автор вопроса
    Делай добро и бросай его в воду.
    В общем, оказалось, что существенно ускорить алгоритм можно, если хоть капельку известна природа исходных данных.
    • Если известно распределение и границы, то почти точно вычисляем порог, выше которого будут наши 10 элементов. Раскладываем массив на две части (выше порога и ниже порога) за O(N). Даже если набрали 11-12 элементов больше порога, то за O(мало) уже смело сортируем и находим 10.
    • Если распределение не известно, но известны границы (то есть минимум и максимум), то делаем предположение, что распределение равномерное, после чего вычисляем порог и задача сводится к предыдущему пункту.
    • Если границы не известны, то за O(N) вычисляем минимум и максимум, после чего задача сводится к предыдущему пункту.

    Конечно, всегда можно специально подсунуть такие данные, что алгоритм будет не эффективен. Но противодействие такому за рамками вопроса.
    Ответ написан
    Комментировать
  • Видел кто-нибудь быстрый алгоритм динамической генерации бесконечного пещерного лабиринта?

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

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

    Далее не понятно, как вы данные храните. Если у вас нет поля заданного размера, то хоть какое-то поле есть? Или каждая клетка/комната/соединение у вас представлены отдельным объектом в куче? И лабиринт по сути не сетка, а граф? От этого алгоритм тоже зависит.
    Ответ написан
  • Как определить, что запись популярная (будет популярной в будущем)?

    dollar
    @dollar
    Делай добро и бросай его в воду.
    Вам нужна нейронная сеть, которая на вход будет получать текст записи, а на выход давать вероятность популярности в будущем. Если в записи изображение, то задача усложняется, но принцип примерно тот же. Тема очень не простая, и дешево вы не сможете такое реализовать.
    Ответ написан
    5 комментариев
  • Как правильно разработать возможность разнообразия характеристик у персонажей?

    dollar
    @dollar
    Делай добро и бросай его в воду.
    Я бы сделал так. Рандомизировал все характеристики для 100000 колобков, дальше запустил бы каждого в 100 случайных ситуаций с волками и лисами, и посмотрел бы, выживут они или нет. Потом оставил бы только тех, у кого 90% выживаемость. Характеры этих живчиков занёс бы в базу, и во время игры спавнил бы из базы случайного колобка.
    Ответ написан
  • Как оптимизировать подгрузку содержимого?

    dollar
    @dollar
    Делай добро и бросай его в воду.
    Если пользователи так много "крутят" колесо мыши, то может лучше сделать нормальный поиск по комментариям? Или фильтр с гибкими условиями. Тогда будут меньше крутить, и миллион сократится до 100 штук внезапно.

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

    То есть надо понять, в каком месте у вас сложность алгоритма O(N) или не дай бог O(N2).
    Ответ написан
    Комментировать
  • Unity как сделать генерацию мира как в terraria?

    dollar
    @dollar
    Делай добро и бросай его в воду.
    Ответ: сложно.

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

    Но в целом нет ничего сложного. Нужно просто сесть и подумать, как конкретно вам для вашей конкретной игры, с учетом геймплея и прочего, будет удобнее это реализовать. И это работа - где-то на стыке главного программиста и дизайнера.
    Ответ написан
    3 комментария
  • Как понять трактовку задания?

    dollar
    @dollar
    Делай добро и бросай его в воду.
    Способ хранения такой есть.

    https://ru.wikipedia.org/wiki/Двоичное_дерево_поиска

    Где-то в задании у вас должно быть пояснение, зачем вам это надо. То есть другие пукнты задания - это поиск и сортировка.
    Ответ написан
    3 комментария
  • Оптимизировать код или как выделить всю вычислительную мощность пк на его выполнение?

    dollar
    @dollar
    Делай добро и бросай его в воду.
    Первое, что бросается в глаза - это многократное копирование массива. Представьте, что при сортировке мы бы после каждых двух-трех перестановок делали бы полный дубликат массива. Это же ужас! И это слабое место, постоянное перевыделение памяти больших размеров.

    Второе, что тоже важно - это сложность O(N*N). В вашем случае это критично, потому что много элементов в исходном массиве.

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

    И маленькая оптимизационная хитрость - поиск интервала происходит по индексу, то есть O(1). Нужно немного поразмыслить, чтобы до этого догадаться, но в целом всё просто.
    Код
    <?php
    $arr = [100,125,75,175,25,300,275,325,375];
    $step = 50;
    
    $b = []; //-1 - deny, 0 - not set, 1 - has interval
    $int = []; //intervals if necessary 
    $step2 = intdiv($step,2);
    $arr = array_values(array_filter($arr, function($v) use ($step2,&$b,&$int) {
        $i = intdiv($v,$step2);
        $mod = $v % $step2;
        $res = true;
        if (isset($b[$i])) {
            if ($b[$i] === -1) $res = false;
            elseif ($mod < $int[$i][0] or $mod > $int[$i][1]) $res = false;
        }
        $b[$i] = -1;
        $b[$i+1] = -1;
        $b[$i-1] = -1;
        if (!isset($b[$i+2])) {
            $b[$i+2] = 1;
            $int[$i+2] = [$mod,$step2];
        } elseif ($b[$i+2] === 1) {
            if ($int[$i+2][0] < $mod) {
                $int[$i+2][0] = $mod;
                if ($int[$i+2][0] >= $int[$i+2][1]) $b[$i+2] = -1;
            }
        }
        if (!isset($b[$i-2])) {
            $b[$i-2] = 1;
            $int[$i-2] = [0,$mod];
        } elseif ($b[$i-2] === 1) {
            if ($int[$i-2][1] > $mod) {
                $int[$i-2][1] = $mod;
                if ($int[$i-2][0] >= $int[$i-2][1]) $b[$i-2] = -1;
            }
        }
        return $res;
    }));
    
    var_dump($arr); // [100, 175, 25, 300, 375]
    ?>

    Переписав алгоритм на С++, получите дополнительно 50-кратное увеличение скорости.
    Ответ написан
    3 комментария
  • Как точнее всего предсказывать комбинацию?

    dollar
    @dollar
    Делай добро и бросай его в воду.
    Это задача поиска закономерности. То есть по сути вам нужно найти алгоритм, формулу, функцию, по которой эти числа генерируются. Это не простая задача.

    Начнём с того, что может быть два варианта: либо формула есть, либо её нет. Случайное число относится к варианту, когда формулы нет. Хотя псевдослучайное число можно описать, но это отдельный, пограничный случай (потому что если число должно быть случайным, но имеется формула, то это по сути ошибка автора алгоритма, и ответ будет зависеть от того, что вам нужно и чем вы занимаетесь - исправлением ошибки или её эксплуатацией).

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

    Таким образом, что же можно простого предложить в вашем случае? Можете сформулировать гипотезу и проверить её. Потом ещё одну - тоже проверить. И так далее.

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

    То есть формулы могут быть, например, такие: (N * 917 mod 512), или же (N * N mod 100 ) и так далее (mod - это остаток от деления, а N - номер числа). То есть это какие-то функции на основе каких-то аргументов. Вам надо придумать, в каком виде их представить как данные, и перебирать по очереди.

    Но, как я сказал выше, формулы может не быть, либо вы её не угадаете даже с перебором, либо она может быть такой сложной, что умный всеобъемлющий перебор займёт годы. Удачи! :)
    Ответ написан
    3 комментария
  • Самый быстрый алгоритм для поиска самого большого значения в неотсортированном массиве?

    dollar
    @dollar
    Делай добро и бросай его в воду.
    в неотсортированном массиве

    O(n)
    То есть полный перебор.

    Но он не используется в продкашене

    Вам нужен самый быстрый алгоритм или соответствие (мнимому) продакшену?
    Ответ написан
    2 комментария
  • Какой алгоритм решения этой задачи можете посоветовать?

    dollar
    @dollar
    Делай добро и бросай его в воду.
    function repairCase(str, substr) {
    	if (!substr) return ''; //Empty string
    	//let arr = []; //Найденные результаты. Для наглядного дебага
    	let str_upper = str.toUpperCase();
    	let sub_upper = substr.toUpperCase();
    	let len = sub_upper.length;
    	let index = 0; //Стартовая позиция
    	let max_score = -1; //Максимальная оценка на совпадение по регистру.
    	let answer = ''; //То, что вернёт функция.
    	while (true) {
    		let result = str_upper.indexOf(sub_upper, index); //Ищем позицию очередного куска
    		if (result == -1) break;
    		let found = str.substr(result, len); //Найденный кусок
    		let score = 0; //Оценка совпадения
    		for(let i=0;i<substr.length;i++) { //Простой алгоритм:: чем больше совпадений, тем лучше.
    			if (substr[i] == found[i]) score++; //Увеличиваем оценку за каждое совпадение по символу.
    		}
    		if (score > max_score) { //Нашли более подходящий результат
    			max_score = score;
    			answer = found;
    		}
    		//arr.push({ pos: result, found: found, score: score });
    		index = result + 1;
    	}
    	//console.log(arr); //Смотрим, что под капотом
    	return answer;
    }

    Примеры вызова:
    repairCase('Hello World', 'hell'); // 'Hell'
    repairCase('Алан Тьюринг', 'а'); // 'а'
    repairCase('Дональд Кнут', 'Н'); // 'н'
    repairCase('Линус Торвальдс', 'ндc'); // ''


    P.S. У меня к вам встречный вопрос: вы программист? Задача решается за 10 минут.
    Ответ написан
    3 комментария
  • Как проверить, что ip находится в заданном диапазоне?

    dollar
    @dollar Автор вопроса
    Делай добро и бросай его в воду.
    const ip4ToInt = ip =>
      ip.split('.').reduce((int, oct) => (int << 8) + parseInt(oct, 10), 0) >>> 0;
    
    const isIp4InCidr = ip => cidr => {
      const [range, bits = 32] = cidr.split('/');
      const mask = ~(2 ** (32 - bits) - 1);
      return (ip4ToInt(ip) & mask) === (ip4ToInt(range) & mask);
    };
    
    const isIp4InCidrs = (ip, cidrs) => cidrs.some(isIp4InCidr(ip));
    
    isIp4InCidrs('192.168.1.5', ['10.10.0.0/16', '192.168.1.1/24']); // true
    Ответ написан
    Комментировать
  • Вопрос по циклу JS и массиву - почему так?

    dollar
    @dollar
    Делай добро и бросай его в воду.
    Потому что arr[j] сравнивается с arr[j+1]. Это два элемента подряд. Значит, j должен проходить не от 0 до максимума, а от 0 до максимума минус 1.

    Если сделать условие j < i, то будет лишняя операция, которая не нужна, хоть и не мешает. Просто у нас от i до arr.length уже отсортированный массив. Поэтому a[j+1] будет равно a[i], но a[i] уже точно на своём месте и его никуда двигать не нужно. Поэтому это просто лишняя операция, для которой arr[j] > arr[j+1] будет заведомо ложно.
    Ответ написан
    Комментировать