• Как выполнить циклический сдвиг битов вправо в C#?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    ((a*257)>>s)&255.
    Будет работать быстрее, почему-то логические операции на C# очень дорогие по сравнению с умножением.
    Ответ написан
  • Как строятся графики по набору данных?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Я обычно делаю так. Предположим, что желаемая форма графика известна - прямая, парабола, синусоида, поверхность второго порядка... Ищу коэффициенты наилучшего приближения по методу наименьших квадратов. Смотрю для каждой точки величину остаточной ошибки, считаю средний квадрат ошибок (сигму). Отбрасываю все точки, для которых ошибка больше 2.5*сигма, для остальных ищу график снова. И так, пока процесс не стабилизируется.
    А вообще надо подробнее написать, что за данные, откуда они взялись, в каком диапазоне, что нужно от графика - например, должен ли он строго проходить через точки, или должен проходить в области наибольшего сгущения, известно ли что-нибудь про зависимость заранее... А так задача слишком общая.
    Ответ написан
    1 комментарий
  • Как найти количество вариантов получения числа из последовательности чисел?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Да, задача не имеет вообще ничего общего с тем, что вы написали вначале. Надо найти число подотрезков - то есть, фрагментов последовательности без пропусков. Ограничения "нельзя удлинять отрезок, если сумма достигла M+1" у них нет - они говорят только, что можно и не удлинять. То, что "воспользуемся модулем", не означает, что надо брать сумму модулей - иначе ответ был бы около 50. Вероятно, имеется в виду, что модуль суммы должен быть больше M (из примера этого понять нельзя, в нём нет отрезка с суммой меньше -8).
    Могу сказать, что задача совсем непростая. Грубой силой она не решается (требуется N^2/2 сравнений, в секунду не уложитесь).
    Я бы делал так. Взять массив частичных сумм (0, a0, a0+a1, ...). Потом сделать log_2(N) копий этого массива (занумерованных индексом k от 1 до log_2(N)), и в каждом из них отсортировать отрезки длиной 2^k. Для массива из задачи это будет выглядеть так:
    S0=S[0]=[0,-2,7,10,16,19,27,26,36,30,37]
    S[1]=[-2,0, 7,10, 16,19, 26,27, 30,36, 37]
    S[2]=[-2,0,7,10, 16,19,26,27, 30,36,37]
    S[3]=[-2,0,7,10,16,19,26,27, 30,36,37]
    К сожалению, массивы S[1],S[2],S[3] оказались одинаковыми - это потому, что в примере мало отрицательных чисел.
    Имея такие массивы, вы легко ответите на вопрос "сколько чисел в фрагменте массива S0[k+1]..S0[N] не принадлежат диапазону S0[k]-M .. S0[k]+M" (время - log(M)^2). Сумма таких ответов для всех k от 0 до M-1 и даст ответ на задачу. Полное время - M*log(M)^2.
    UPD. Лучше бы они писали условие по-английски. Было бы не так стыдно за формулировку.
    Ответ написан
  • Как выбрать какую функцию вызвать на этапе выполнения программы?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Например, так:
    double FindMinimum(Func<double,double,double>F,out double x1,out double x2){
         x1=x2=0;
         return F(x1,x2);
    }

    Тогда вызов поиска минимума будет double zmin=FindMinimum(f1,out x,out y);
    Ответ написан
    Комментировать
  • Как решать задачу на счастливые билеты?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Как-то так:

    int S0[37],S1[37],S2[37],S3[37],S4[37];
    
    void Expand(int *a,int *b,int n){
    	int s0=0;
    	for(int i=0;i<n+10;i++){
    		if(i<n) s0+=a[i];
    		if(i>=10) s0-=a[i-10];
    		b[i]=s0;
    	}
    }
    
    int Conv(int *S,int balance,int m){
    	int res=0;
    	while(--m>=0) res+=S[m]*S4[balance+m];
    	return res;
    }
    
    // number of tickets in 0..u-1
    int NHappy(int u){
    	int res=0;
    	int balance=0;
    	int digit;
    
    	digit=u/10000000; u%=10000000;
    	for(int i=0;i<digit;i++) res+=Conv(S3,balance++,28);
    	digit=u/1000000; u%=1000000;
    	for(int i=0;i<digit;i++) res+=Conv(S2,balance++,19);
    	digit=u/100000; u%=100000;
    	for(int i=0;i<digit;i++) res+=Conv(S1,balance++,10);
    	digit=u/10000; u%=10000;
    	for(int i=0;i<digit;i++) res+=S4[balance++];
    	digit=u/1000; u%=1000;
    	for(int i=0;i<digit && balance>=0;i++) res+=S3[balance--];
    	digit=u/100; u%=100;
    	for(int i=0;i<digit && balance>=0;i++) res+=S2[balance--];
    	digit=u/10; u%=10;
    	for(int i=0;i<digit && balance>=0;i++) res+=S1[balance--];
    	if(balance>=0 && balance<u) res++;
    	return res;
    }
    
    void __cdecl main(){
    	S0[0]=1;
    	Expand(S0,S1,10);
    	Expand(S1,S2,19);
    	Expand(S2,S3,28);
    	Expand(S3,S4,37);
    
    	int m,n;
    	for(;;){
    		printf("m,n: ");
    		scanf("%d%d",&m,&n);
    		printf("Result: %d\n",NHappy(99999999)+1-NHappy(m)-NHappy(99999999-n));
    	}
    }

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

    UPD: Вот оптимизированный вариант:
    int S[8][38];
    
    void InitS(){
    	for(int i=1;i<38;i++) S[0][i]=1;
    	for(int a=1;a<9;a++){
    		int s0=0;
    		for(int i=1;i<=37;i++){
    			s0+=S[a-1][i];
    			if(i>10) s0-=S[a-1][i-10];
    			S[a][i]=s0;
    		}
    	}
    }
    
    // number of tickets in 0..u-1
    int NHappy(int u){
    	int res=0;
    	int balance=36;
    	for(int a=7,b=10000000;a>=0;a--,b/=10){
    		int digit=u/b;
    		u%=b;
    		int b1;
    		if(a>3){
    			balance-=9;
    			res+=S[a][b1=balance+digit]-S[a][balance];
    		} else{
    			b1=balance-digit; if(b1<0) b1=-1;
    			res+=S[a][balance+1]-S[a][b1+1];
    		}
    		balance=b1;
    	}
    	return res;
    }
    
    void __cdecl main(){
    	InitS();
    	int m,n;
    	for(;;){
    		printf("m,n: ");
    		scanf("%d%d",&m,&n);
    		printf("Result: %d\n",NHappy(99999999)+1-NHappy(m)-NHappy(99999999-n));
    	}
    }

    Не спрашивайте, почему :)
    Ответ написан
    3 комментария
  • Является ли наличие двух одинаковых констант в коде признаком не оптимальности решения?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Зависит от длины константы. Что короче - загрузить её в регистр непосредственно в команде, или взять из памяти (указав в команде адрес)? Сильно зависит от процессора.
    В целом, одинаковые константы в алгоритме это не страшно. Хуже, когда одно и то же условие приходится проверять много раз. Или, что ещё страшнее, условия, следующие одно из другого (проверили, что a < b, а потом проверяем, что a+c < b+c). Такое часто встречается в геометрии. И в этом случае над алгоритмом стоит поработать.
    Ответ написан
    Комментировать
  • Как обстоят дела с программным обеспечением на огромных объектах?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    На авианосце, наверняка, огромное количество блоков управления, датчиков, микроконтроллеров и микропроцессоров, а так же более крупных управляющих узлов. И для каждого из них нужна своя программа. А может быть, и специально настроенная операционная система. Все ли датчики и блоки управления дублируются - вероятно, зависит от их важности. Иногда дешевле и надёжнее продублировать, иногда поставить периодическое тестирование, иногда предусмотреть работу при отказавшем узле. Предусмотрено ли несколько версий программ управления на случай "если в одной версии оказалась ошибка - поставим другую" - не уверен. Разве что на время очередного апгрейда и тестирования в реальных условиях.
    Но это всё догадки.
    Ответ написан
    Комментировать
  • Какие ресурсы "экономить" при алгоритмизации?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Если вы хотите экономить память, то проблема будет не в переменных, а в больших структурах (списки, массивы, словари...). Мне чаще всего встречаются такие ситуации:
    - память в обмен на время. Запоминать результаты или вычислять заново? Иногда здесь удаётся обменять экспоненциальное время на полиномиальное, или уменьшить степень полинома на 1 или на 2. Но чаще выигрыш идёт только в константу раз (хотя в узком месте это может быть немаловажно). Впрочем, иногда вычислить функцию оказывается быстрее, чем найти её значение в таблице - так что надо такие оптимизации проверять.
    - замена обработки большого массива в памяти на послойную обработку (когда очередной фрагмент/строка/двумерный слой трёхмерного массива... вычисляется, обрабатывается, а уже ненужный слой забывается). Здесь память экономится за счёт серьёзного усложнения кода - приходится писать всякие конвейеры. Зато появляется шанс сэкономить ещё и время с помощью параллельной обработки.
    - хранение данных, которые должны быть в памяти, в упакованном виде. Трудно читать, очень трудно писать, код усложняется, время теряется. Но появляется шанс уложиться в 3 ГБ и остаться в 32-битном режиме (если это важно).
    - хранение в памяти против чтения из файла. Либо будет теряться время, либо усложняться код (если сумеете воспользоваться асинхронным чтением/записью).
    - в тяжелых случаях - экономия байтов кода (это если пишете на ассемблере). Может пригодиться, если действительно пишете сервер для кофеварки. Но возможно, здесь лучше попытаться поставить процессор помощнее.

    Сначала пишите, как проще, но если есть риск развития проекта за пределы текущих ресурсов, предусмотрите возможность постепенного перехода на более экономные по памяти (или, там где надо - по времени) алгоритмы.
    Ответ написан
    2 комментария
  • Как правильно закодить раскраску графа в с++?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Рекурсией. Находите нераскрашенную вершину, у которой меньше всего вариантов раскраски, и красите её во все возможные цвета. После каждой покраски - рекурсивно вызываете эту же функцию. Если оказалось, что неиспользованных цветов столько же, сколько нераскрашенных вершин - красите каждую в отдельный цвет и возвращаете как решение.
    Ответ написан
    Комментировать
  • Как написать функцию удаления лишних пробелов в строке?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Лишние пробелы - это те, которых больше одного подряд?
    List *delSpaces(List *p) {
        for(List **a=&p;*a;){
            if((*a)->c==' ' && (*a)->next && (*a)->next->c==' ') *a=(*a)->next;
            else a=&((*a)->next);
        }
        return p;
    }

    Не проверялось.
    Ответ написан
  • Как сократить число дуг в орграфе с сохранением "степени" вершин?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Проще всего - найти произвольный цикл, в нём - ребро наименьшего веса, и вычесть этот вес из всех рёбер. Продолжать, пока циклов не останется. Но результат может быть сильно неоптимальным.
    Можно искать цикл, в котором наименьший вес ребра максимален (добавлять рёбра начиная с самого тяжёлого, пока в графе не появится цикл). Тогда результат должен получиться заметно лучше (по сумме весов). Но число рёбер может быть не минимальным.
    Кстати, можно ли увеличивать веса дуг (из графа (1,2,w=1), (2,3,w=2), (1,3,w=4) получить (2,3,w=1),(1,3,w=5))? Если да, то после того, как циклы в ориентированном графе кончились, придётся искать циклы в неориентированном графе, и прибавлять ко всем рёбрам наименьший по модулю вес ребра с учётом направления рёбер (вычитать из попутных и прибавлять ко встречным).
    Ответ написан
  • Найти неизвестную вторую точку?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Если расстояние равно d, то вторая точка имеет координаты x=10+d*cos(t), y=10+d*sin(t), где t - какое-то число. Если на вторую точку есть дополнительные условия, то координаты иногда можно найти точнее.
    Ответ написан
    1 комментарий
  • Возможно создать искусственный интеллект используя современные технологии?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Множество "всех случаев жизни" имеет тенденцию расширяться. ИИ придётся научиться самому писать скрипты для новых ситуаций. А для этого - находить в этой ситуации объекты, их взаимосвязи, закономерности поведения и реакцию на действия ИИ. И после этого решать задачу оптимального управления. И всё это в реальном времени.
    Ответ написан
    Комментировать
  • Как составить алгоритм решения задачи на ДП или Рекурсию (Задача на лесенку)?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Решить задачу легко - заводите массив A[N,N], в каждой клетке [K,M] которого будет записано число лесенок из K кубиков, нижний слой которых состоит из M кубиков. Число будет ненулевым, когда M <= K <= M*(M+1)/2.
    Массив заполняется начиная со строчки K=1 по формуле A[K,M]=sum(A[K-M,r],r=1..M-1). Сумма элементов в строчке K=N будет искомым числом.

    В решении, ссылку на которое вы дали, последовательно вычисляется число лестниц из j кубиков, нижний ряд которых не длиннее, чем i кубиков. Чтобы найти это число, надо к уже найденным лестницам, нижний ряд которых не длиннее, чем i-1 кубик (их мы оставляем без изменения), прибавить лестницы из j-i кубиков с нижним рядом не длиннее, чем i-1 кубик (к ним мы добавим ряд из i кубиков). Что и делается во внутреннем цикле.
    Ответ написан
    5 комментариев
  • Полезен ли алгоритм определения НЕпростоты числа с 3 операций?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Надеюсь, что остаток берётся всё-таки от деления на 390, а не на 389 - иначе смысла в коде вообще не видно.
    Итак, у нас есть 77 простых чисел, меньших 390. Четыре из них - 2,3,5,13 - не более, чем мусор, дающий лишние срабатывания алгоритма. В самом деле, если x%390==3, то x делится на 3, а значит, оно составное. Остальные 73 числа годятся.
    Получается такая картина. Для простого x величина x%390 - некоторое число, взаимно простое с 390. Причём распределены эти остатки более-менее равномерно (на картинке они выглядят как красные столбики). Всего этих остатков 96.
    Если x - простое число, то с вероятностью 73/96 остаток будет простым числом, и алгоритм честно скажет "скорее, простое". С вероятностью 23/96 - те самые 25% - остаток окажется составным, и алгоритм ошибётся.
    Если x - составное, то вероятность того, что число объявят "скорее, простым" будет равна 77/390-1/ln(x) (первое слагаемое - вероятность того, что остаток оказался простым, а второе - доля простых чисел в окрестности x).
    Можно легко избавиться от ошибки первого вида: для этого надо в массив charr положить не простые числа, а числа, взаимно простые с 390. Тогда если алгоритм скажет "составное", то так и есть.
    Можно было вместо 390 взять N=30030=2*3*5*7*11*13. Если массив будет заполнен числами, взаимно простыми с N, то отсеиваться, как составные, будет 4 числа из 5, а ложных отсеиваний простых чисел не будет вообще.
    Ответ написан
    2 комментария
  • Что такое машина Тьюринга и какое отношение она имеет к программированию?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    В первую очередь - это формальное определение алгоритма. Задача считается алгоритмически разрешимой тогда и только тогда, когда её решение можно запрограммировать на машине Тьюринга (или каким-нибудь другим эквивалентным способом). Это определение даёт, например, возможность предъявить алгоритмически неразрешимые задачи. Позволяет ввести понятие "Тьюринг-полного" языка - если на языке можно реализовать машину Тьюринга, то на нём можно написать любой алгоритм (язык С таким не является, а C# - является).
    В общем, МТ - способ определить некоторый класс алгоритмов:
    - некоторые задачи можно решить конечным автоматом;
    - для некоторых потребуется конечный автомат со стековой памятью;
    - для других достаточно машины Тьюринга;
    - для остальных требуется божественное откровение или другие неалгоритмизируемые методы.
    Ответ написан
    7 комментариев
  • Как оптимально найти подмножества в наборе данных многие-ко-многим?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Есть простое решение за (число сообществ)*(число связей "пользователь-сообщество").

    Для каждого сообщества Y:
    - заводим массив R[C], где C - число сообществ
    - для каждого пользователя X из сообщества Y:
    - - для каждого сообщества M, в которое входит пользователь X: R[M]=R[M]+1
    - для каждого сообщества M: если M!=Y и R[M] > 1, то пара (Y,M) - ядро.

    Быстрее пока не получается.
    Ответ написан
    Комментировать
  • Как получить массив случайных чисел, сумма которых равна 500?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Предположим, что числа нужны целые неотрицательные. Если числа могут быть нулевыми, делаете так:
    n1=irand(501); // от 0 до 500
    n2=irand(501); // от 0 до 500
    n3=irand(501); // от 0 до 500
    sort(n1,n2,n3); // сортируете по возрастанию любым подходящим способом
    x0=n1; x1=n2-n1; x3=n3-n2; x4=500-n3;

    Если числа могут быть только положительными, то поступаете аналогично:
    n1=irand(497); // от 0 до 496
    n2=irand(497); // от 0 до 496
    n3=irand(497); // от 0 до 496
    sort(n1,n2,n3); // сортируете по возрастанию любым подходящим способом
    x0=n1+1; x1=n2-n1+1; x3=n3-n2+1; x4=496-n3+1;

    Распределение будет слегка неравномерным (наборы, содержащие нули в первом случае и единицы - во втором, будут встречаться чуть реже), но и это можно исправить, слегка усложнив программу.
    Ответ написан
    Комментировать
  • Переменная vs цикл в данном случае?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Зависит от статистики обращений.
    Если проверки идут намного чаще, чем изменения массива - то переменная. Причём при "удалении" цифры придётся не просто очищать ячейку, а пройтись по всему массиву - нет ли там ещё одной такой цифры.
    Если проверки и изменения идут сериями (много изменений - много проверок), то две переменных. В одной - индекс, в другой признак того, не испортился ли он. Тогда при проверке с "испортившимся" индексом придётся пройтись по массиву и посмотреть, где теперь цифра. Хотя можно просто проверить, что лежит сейчас в ячейке с индексом. Если не нужная цифра - новый проход по массиву. Но здесь уже надо смотреть, какая ячейка портится чаще - та, в которой цифра появилась раньше всего, или та, где она появилась позже всего.
    Если проверки идут достаточно редко, то цикл.
    В сложных случаях - когда в массиве не 10, а 1000 ячеек, цифр может быть много, меняются они часто, и нужно часто проверять, есть ли цифра, и где именно - надо будет хранить индексы ячеек с этими цифрами в какой-нибудь структуре. Например, в двусвязном списке, построенном на двух массивах, параллельных рабочему. Тогда все изменения и проверки будут работать за O(1).
    В общем, всё зависит от ситуации.
    Ответ написан
    Комментировать
  • Как сгенерировать загадку эйнштейна?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Математика здесь не нужна, достаточно программирования.
    Посмотрите на игрушку Sherlock: www.kaser.com/sherwin.html
    Там алгоритм решения полностью открыт: есть трёхмерный битовый массив "может ли в доме A признак B иметь значение C", и в каждый момент решения найдётся подсказка, исключающая один из вариантов. Так что вам нужно сначала сгенерировать любой набор подсказок, приводящий к решению по этому алгоритму ("если зашли в тупик - добавим ещё подсказку"), а потом исключать из полученного набора те подсказки, без которых задачу можно решить (исключаете по одной и пытаетесь решить). Учтите, что подсказки есть разной силы, и чтобы не получилось огромного набора из "A не сосед B", надо соблюдать баланс, добавив (лучше, сначала, но не обязательно) несколько более сильных подсказок.
    Лучше сгенерировать сотню-другую наборов заранее, а потом применять их, перемешивая признаки и значения в случайном порядке.
    Ответ написан
    Комментировать