• Раз Пи бесконечно, можно сказать, что его значение меняется постоянно?

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

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Английский там простой. Даже чересчур простой (что не удивительно, учитывая, что это писали французы) - и в результате этого упрощения существенные детали скрылись или потерялись.
    Общая идея понятна: мы вычисляем номер корзины при хешировании как K=C%N (где C - значение hash key), а когда корзина переполняется, увеличиваем для неё N в 2 раза, и перекидываем часть элементов (для которых C%(2*N)!=K) в корзину с номером K+N. У корзины как-то запоминаем актуальное для неё значение N=N0*2^j (точнее, запоминаем j).
    Дьяволы, как обычно, прячутся в деталях, и эти детали при первом прочтении мне понять не удалось:
    - до переполнения в корзине может храниться более одного элемента. Где они хранятся? Для них сразу зарезервировано место, или где-то есть место для хранения списка?
    - Что они делают при увеличении N? Неужели удваивают размер всей таблицы? Или каким-то образом отводят место только для новых корзин - потомков переполнившихся?
    Не знаю, какая у Вас цель, но я бы в этот момент придумал какую-нибудь схему, рещающую эти вопросы (вероятно, представил бы корзины в виде бинарного дерева, где пути влево-вправо определяются битами в последовательности остатков C%(N*2^j)), не пытаясь разобраться в статье дальше. Но если нужно разобраться именно в этом алгоритме - придётся его читать.
    Кстати, какого года статья? По общему впечатлению (и по датам цитируемых работ), это где-то 1979-1985 гг. Не забывайте, что тогда "640 КБ хватало всем"!
    Ответ написан
    1 комментарий
  • Сколько нужно завести друзей в Facebook, чтобы с вероятностью близкой к 100% каждый день в году поздравлять с днем рождения хотя бы одного друга?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Если надо найти точную вероятность того, что все дни рождения заполнены, то формула получается сложной. Эквивалентная формулировка - найти количество M-значных чисел в системе счисления по основанию N, в которых встречаются все N цифр.
    Рассматриваем наборы из N чисел a1,a2,a3,...,aN, для которых ai>=0 и a1+a2+...+aN=M-N, и по всем таким наборам (их С(M-1,N-1), наборы с разным порядком считаются разными) считаем сумму S величин 1^a1+2^a2+...+N^aN. Тогда искомое количество чисел равно S*N!, а искомая вероятность - S*N!/(N^M).
    Приближенную вероятность, когда M заметно больше N, найти проще. Вероятность того, что в определённый день ни одного дня рождения не будет, равна (1-1/N)^M, и вероятность того, что хотя бы один день рождения будет каждый день - (1-(1-1/N)^M)^N (здесь мы считаем события независимыми, что вообще говоря, неправда). При больших N и M/N это можно оценить как exp(-N*exp(-M/N)). Если N=365, то для достижения вероятности 99% нужно 3833 друга, а для 99.9% - 4675 друзей.
    Наличие 29 февраля ещё несколько усложняет картину.
    Ответы на вопросы 1-5 при N=366: 2041, 2295, 2617, 3844, бесконечность.

    Уточнение: число S, которое получается в точном решении, это число Стирлинга второго рода S(M,N). По ссылке есть простая явная формула.
    Ответ написан
    3 комментария
  • Как повернуть уравнение плоскости относительно точки?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    повернуть уравнение плоскости на угол Y U W (относительно осей ox, oy, oz)

    - что имеется в виду? Повернуть плоскость на угол Y относительно ox, потом на U относительно oy, и потом на W относительно oz?
    Предположим, что так.
    Первым делом, надо сдвинуть систему координат так, чтобы точка M оказалась началом координат:
    x=x1+MX, y=y1+MY, z=z1+MZ. Уравнение плоскости в этой системе будет A*x1+B*y1+C*z1+(D+A*MX+B*MY+C*MZ)=0.
    Теперь выполним поворот относительно оси Ox: A1=A, B1=B*cos(Y)+C*sin(Y), C1=-B*sin(Y)+C*cos(Y). Свободный член в уравнении не изменится (расстояние до начала координат и длина вектора нормали не изменились), получилось уравнение A1*x+B1*y+C1*z+D1=0 (где D1=D+A*MX+B*MY+C*MZ). Аналогично выполняем повороты относительно Oy и Oz. Получится уравнение A3*x+B3*y+C3*z+D1=0. Осталось сдвинуть систему координат обратно, чтобы начало координат перешло в точку M: A3*x+B3*y+C3*z+(D1-A3*MX-B3*MY-C3*MZ)=0. Это есть ответ. Надо только убедиться, что повороты идут в правильные стороны.
    Ответ написан
    Комментировать
  • Как определить кривизну линии?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Если надо проверить среднее отклонение от прямой, то можно посчитать матрицу ковариации:
    xm=sum(x[i])/N; ym=sum(y[i])/N;
    a=sum((x[i]-xm)^2); b=sum((x[i]-xm)*(y[i]-ym)); c=sum((y[i]-y0)^2);
    и посчитать отношение собственных значений:
    R=(a+c-sqrt((a-c)^2+4*b^2)/(a+c+sqrt((a-c)^2+4*b^2).
    Если R достаточно мало (скажем, меньше 0.01), то считаем, что это прямая.

    Если надо проверить, прямая это или дуга, то берём крайние точки (x0,y0) и (xn,yn). Пусть dx=xn-x0,dy=yn-y0.
    Считаем для всех точек
    z[i]=((x[i]-x0)*dx+(y[i]-y0)*dy)/sqrt(dx^2+dy^2), w[i]=((x[i]-x0)*dy-(y[i]-y0)*dx)/sqrt(dx^2+dy^2),
    Теперь приближаем зависимость w(z) параболой (по методу наименьших квадратов): w=A*z^2+B*z+C. Величина A пропорциональна кривизне дуги, по которой идут точки.
    Ответ написан
    Комментировать
  • Развивается ли способность к решению нестандартных задач?

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

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Боюсь, что будет очень трудно. Все эти юнит-тесты, соблюдение стилей программирования, документация, системы поддержки версий через какое-то время заставят забыть, что ты математик. Я сейчас стараюсь держаться от них подальше: попал на такое место, где нужен именно математик-алгоритмист, и, хотя пишу много кода, программистом себя считать не могу. Это опасное положение: если придётся менять работу, будет трудно найти что-нибудь подобное.
    Так что чтобы переквалифицироваться в программиста, надо изучать современное программирование с нуля. А чтобы научиться придумывать и реализовывать алгоритмы - брать книжку по алгоритмам, и разбирать и писать их все подряд. Через какое-то время придумать себе задачу с околоматематической формулировкой, попытаться её решить (пример: построить все неэквивалентые триангуляции многообразий - с краем и без, содержащие данное число треугольников). Потом другую задачу, и т. д.
    Ответ написан
    Комментировать
  • Как получить нужное число, который получается в результате сложения чисел из указанного массива?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    В вашем примере каждое из чисел, кроме 5, делится на предыдущее. Поэтому, до определённого момента - пока осталось заплатить не меньше 10 руб, и в кошельке есть купюры не меньше 10 - можно пользоваться "жадным" алгоритмом - брать самую большую купюру, которая меньше остатка стоимости товара.
    Допустим, что остались только монеты по 1,2,5 руб. Если есть хотя бы одна монета 1 руб - то платить пятирублёвой монетой безопасно, даже если останется нечётная цена - то набрать её рублём и двушками удастся. Если есть две монеты по 5 руб, и осталось заплатить не меньше 10 - смело тратьте одну из них. Вторую тратить подождите, это может быть опасно.
    В конце концов у вас останутся только пятачки и двушки, причём либо пятачков не более одного, либо осталось заплатить меньше десятки. Смотрите. Если надо заплатить нечётную сумму: если пятачков нет или надо заплатить 1 или 3 рубля - задача неразрешима. В противном случае тратите пятачок, остаётся чётная сумма.
    Пытаетесь набрать остаток двушками. Если их хватило - вы победили. Если нет, то вам подсунули неразрешимый пример...
    Если бы набор купюр и монет был из времён СССР (1,2,3,5,10,15,20,50,100,300,500,1000,2500,5000,10000), задача была бы значительно сложнее и интереснее.

    UPD. Моё решение неверно. Кто найдёт контрпример?

    Более правильный вариант.
    - если есть хотя бы одна монета в 1 руб, то работает "жадный" алгоритм - в цикле платите самой большой купюрой, которая у вас есть и не превышает остатка суммы.
    - если монеты 1 руб. нет:
    - - если сумма, которую надо набрать, нечётна, а пятачка нет, вы проиграли.
    - - если сумма, которую надо набрать, нечётна, и есть хотя бы один пятачок, вносим его. Переходим к следующему пункту.
    - - если остаток суммы, который надо набрать, чётен - объединяем пятачки в пары (и по одному пятачку платить не будем, даже если хочется). И запускаем жадный алгоритм. Если задача разрешима, то он справится.
    Ответ написан
    6 комментариев
  • Все ли обыкновенные дифференциальные уравнения научились решать?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Вы имеете в виду "решать аналитически, находить решение в виде элементарных функций"? Очевидно, не все, поскольку существуют неберущиеся интегралы. Например, уравнение y'(x)=sin(x)/x решить нельзя, приходится вводить новую функцию - интегральный синус. Насколько я помню, алгоритм для проверки "берётся ли данный интеграл в заданном множестве функций" существует. Для ОДУ общего вида 20 лет назад такого алгоритма ещё не было, появился ли он с тех пор - не знаю.
    Ответ написан
    Комментировать
  • Что делать с кватернионом при смене базиса?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    У кватерниона изменится ось вращения (она отразится симметрично) и направление поворота. Таким образом, кватернион (x,y,z,w) перейдёт в (-x,y,z,-w).
    Ответ написан
  • Как правильно реализовать регулярное выражение без конечных автоматов?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Как-нибудь так. Не очень эффективно, правда. И без скобок.
    static string FindReg(string src,string ptrn) {
                int LP=ptrn.Length,LS=src.Length;
                for(int i=0;i<LS;i++) {
                    int k=i;
                    bool ok=true;
                    for(int j=0;ok && j<LP;) {
                        int m=1;
                        bool plus=false;
                        char cur=ptrn[j++];
                        for(;j<LP;j++) {
                            if(ptrn[j]=='+') plus=true;
                            else if(ptrn[j]==cur) m++;
                            else break;
                        }
                        int s=0;
                        while(k+s<LS && src[k+s]==cur) s++;
                        if(s<m) {
                            ok=false; break;
                        } else if(plus) k+=s;
                        else k+=m;
                    }
                    if(ok) return src.Substring(i,k-i);
                }
                return null;
            }

    Но вообще, конечный автомат понадобится очень быстро. Уже для какого-нибудь a*b*ac без него трудно будет обойтись (если не писать страшную рекурсию).
    Ответ написан
    Комментировать
  • Как построить сложный алгоритм на комбинаторику?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    static int sstep;
           static void FindAll(int N,int sum,int step,bool ord){
                sstep=step;
                sum/=step;
                fill(new int[N],N,sum,sum-N+1,ord);
            }
            static void output(int[] A){
                  Console.Write("{0}",A[0]*sstep);
                  for(int i=1;i<A.Length;i++) Console.Write("/{0}",A[i]*sstep);
                  Console.WriteLine();
            }
    
            static void fill(int[] A,int N,int sum,int smax,bool ord) {
                if(N==0 && sum==0) {
                    output(A);
                    return;
                }
                if(N==0 || sum<N) return;
                int smin=ord ? 1 : (sum-1)/N+1;
                N--;
                for(A[N]=smin;A[N]<=smax;A[N]++) {
                    fill(A,N,sum-A[N],ord ? sum-N+1 : A[N],ord);
                }
            }

    Задаётся A=new int[N], и вызывается fill(A,N,sum,sum-N+1,ord), где N=6, sum=300, ord - true, если с учётом порядка и false, если без учёта. Я проверял для N=5, sum=10 - там число вариантов приемлемо. Насколько я понимаю, для (6,300,true) будет примерно 21 миллиард комбинаций (Comb(299,5)), а для (6,300,false) - не меньше 30 миллионов.
    Ответ написан
  • Как распределить грузы по вагонам равномерно?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    (Про исходную задачу)
    После того, как отправили самые тяжелые грузы (которые больше средней загрузки вагона), пересчитываем среднее арифметическое, и начинаем укладывать грузы в вагоны, начиная с самого тяжелого. Возможны три стратегии.
    1) Очередной (самый тяжелый из оставшихся) груз кладём в самый пустой вагон
    2) Очередной груз кладём в самый загруженный из вагонов, в которые этот груз ещё помещается (т.е. общий вес вагона и груза не превосходит среднего арифметического)
    3) Очередной груз кладём в случайный из вагонов, в которые груз помещается.
    В стратегиях (2,3) иногда будет возникать ситуация "груз положить некуда". Тогда кладём его в самый пустой вагон, и отправляем.
    Дальше надо экспериментировать, чтобы выбрать более подходящую из этих стратегий. Интуиция тут не очень помогает.

    Если у вагона есть предел загрузки L, то груз, который не умещается даже в самый пустой вагон, оставляем до следующего поезда. При его загрузке сначала раскидываем очень тяжелые грузы, потом - оставленные от предыдущего поезда, потом - все новые.
    Ответ написан
    Комментировать
  • Расчет количества замощений прямоугольника меньшими прямоугольниками?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Здесь достаточно перебора с помощью рекурсии.
    Вам нужно заполнить прямоугольник M*N плитками размером 1*2.
    Берёте массив A[M,N], заполняете его нулями.
    Вызываете функцию Fill(A,1).
    Fill(A,k){
    Ищете первый элемент A[p,q], равный нулю.
    Если не нашли - массив заполнен, печатаете его. return.
    Если элемент A[p+1,q] существует и равен нулю, то в прямоугольник можно положить горизонтальную плитку: в A[p,q] и A[p+1,q] кладёте число k, вызываете Fill(A,k+1). Обнуляете A[p,q] и A[p+1,q].
    Если элемент A[p,q+1] существует и равен нулю, то в прямоугольник можно положить вертикальную плитку: в A[p,q] и A[p,q+1] кладёте число k, вызываете Fill(A,k+1). Обнуляете A[p,q] и A[p,q+1].
    }
    Если бы надо было считать число способов заполнения, надо было бы смотреть в сторону динамического программирования.
    Ответ написан
  • Как кластеризовать точки по принадлежности к разным (неизвестным) прямым?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Если времени не очень жалко, можно попробовать так:
    Перебираем углы от 0 до 180 с шагом, например, 1 градус.
    Проектируем точки на прямую, идущую под этим углом к горизонтали.
    Ищем на проекции достаточно короткий отрезок, в который попало достаточно много точек.
    Ищем методом наименьших квадратов прямую, наилучшим образом приближающую точки, попавшие в этот отрезок.

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

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

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

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Если это C++, то подойдёт такая формула:
    M[i][j]=(abs(j-i)+1)*((i+j-N+1)*(i-j)>=0)
    Если формулу хочется чисто математическую, то вместо ((i+j-N+1)*(i-j)>=0) можно написать (sign(2*(i+j-N+1)*(i-j)+1)+1)/2
    Ответ написан
    Комментировать
  • Как, используя алгоритм быстрой сортировки, определить лежит точка в массиве отрезков или нет?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    Можно решить и за линейное время (после сортировок):
    1) берём массив концов отрезков (xi,1) и (yi,-1), сортируем (не забывая, что при одинаковых первых числах сначала идут все начала отрезков, а потом все концы: так для отрезков [1,2] и [2,3] получим массив (1,1),(2,1),(2,-1),(3,-1))
    2) берём массив длиной n (число точек), заполняем его числами от 1 до n (если пишем на C-подобных языках - то от 0 до n-1). Cортируем массив, используя условие less(a,b)=(P[a] < P[b]), где P - массив точек. Получим индексы точек, отсортированные по возрастанию координат соответствующих точек.
    3) Идём по обоим массивам, сравниваем координаты. Когда проходим точку в массиве концов, то прибавляем к счётчику второй элемент. Когда достигаем координаты очередной точки из P - записываем в массив результатов значение счётчика. Не забываем, что точка P[a]=x обрабатывается после всех точек (x,1) но до всех точек (x,-1) из массива концов.
    4) Выводим массив результатов.
    Ответ написан
    2 комментария
  • Каким будет время работы построения кучи?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    О(N*log(N)). Для каждого из последних N/2 элементов нужно примерно по log(N) операций.
    Вот построить кучу, переставляя элементы уже заполненного массива, можно за O(N) операций.
    Ответ написан
    5 комментариев
  • Как расположить функции в порядке увеличения скорости роста?

    Mrrl
    @Mrrl
    Заводчик кардиганов
    f4 - это sqrt(n) или sqrt(5)? А то в разных местах по-разному.
    Если предположить, что sqrt(n), то в вашем ответе не на месте функция f1. Она растёт гораздо медленнее, чем вы думаете.
    Ответ написан
    1 комментарий