Страницы

Поиск по вопросам

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

суббота, 11 апреля 2020 г.

Как превратить нарисованную от руки линию в красивый график?

#графика #математика

                    
Для своего диплома я хочу сделать компонент (если не получится, то фиг с ним:), который
при рисовании линии (я использую WPF InkCanvas) превращал бы эту линую в график. Ну
то есть выпрямлял посильнее. Не силен в математике, поэтому не знаю, что мне нужно
использовать для этого. В качестве входных данных имеется набор точек. Что можно применить?
    


Ответы

Ответ 1



Для этих целей уже есть готовый алгоритм Рамера-Дугласа-Пекера Он вроде применяется в векторных графических редакторах и картографии. И еще список алгоритмов делающих тоже самое: Visvalingam–Whyatt algorithm Reumann–Witkam algorithm Opheim simplification algorithm Lang simplification algorithm Затем полученную ломанную можно превратить в красивую кривую каким-нибудь алгоритмом интерполяции или использовать точки как опорные для построения кривых Безье.

Ответ 2



Решение в лоб: взять крайние точки исходной линии, найти равно распределенные точки "изломов". Получаем промежутки-условия. Потом для каждого такого промежутка ищем точки, которые в него попадают и находим простейшее среднеарифметическое значение для X и для Y. Получим усредненные значения на участках, соединим их точками - тот же график, но менее детализированный. Больше точек - больше изломов - картинка ближе к графику.

вторник, 31 марта 2020 г.

Математические правила для генерирования звука плавно нарастающей частоты

#математика #аудио


Я пишу программу, которая генерирует WAV-файл, который содержит плавно меняющийся
звук от частоты seq1 до seq2, частотой дискретизации sampleRate = 44100 и длинной n
секунд. Необходимо найти формулу, по которой вычислять частоту текущего отрезка длинной
в одно полное колебание звука, а так же общее количество отрезков frame.  

Текущая частота - seq = k1 * x + seq1, где k1 - скорость роста частоты, x - порядковый
номер отрезка в одно полное колебание;  

Число семплов на отрезок - spt = sampleRate/seq = sampleRate/(k1*x+b);  

P.S. В принципе, можно упростить, отбросив начальную частоту, и считать по формуле
spt=(sampleRate/k1)/x  

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



Но я точно не уверен.  

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

int main() {
    ofstream out("test.wav", ios::binary);

    int time = 30;
    waveHeaderInit(2, 44100, 16, time);
    waveData = new char[waveHeader.subchunk2Size];
    int sequence = 100;
    volatile float phase;
    volatile int16_t sample;

    int maxAmplitude = pow(2, waveHeader.bitsPerSample - 1) - 1;

    for (int i = 0; i < time * waveHeader.sampleRate; i++) {
        int spt = waveHeader.sampleRate / sequence;
        phase = (i % spt) / float(spt);
        sample = sin(PI*phase)*maxAmplitude;
        for (int channel = 0; channel < waveHeader.numChannels; channel++) {
            ((int16_t*)waveData)[i*waveHeader.numChannels + channel] = sample;
        }
    }

    out.write((char*)&waveHeader, sizeof(waveHeader));
    out.write(waveData, waveHeader.subchunk2Size);
    out.close();
    return 0;
}

    


Ответы

Ответ 1



Увеличивайте частоту медленно с экспоненциальной скоростью. Монотонный звук это A*sin(2Pi*B*t). Заменяем константу B на экспонету B:=C*Exp[D*t]. C*Exp[D*t0]==seq0 C*Exp[D*t1]==seq1 --- D=Ln[seq1/seq0]/(t1-t0) C=seq0*((seq0/seq1)^(t0/(t1-t0))) Получаем A*sin(2Pi*C*Exp[D*t]*t). Упрощаем t0=0, t1=T.Результат: A*sin(2Pi*seq0*((seq1/seq0)^(t/T))*t)

понедельник, 30 марта 2020 г.

Условие выхода из рекурсивной функции (считаем косинус через ряд Маклорена)

#python #алгоритм #математика #рекурсия


Отвечая на этот вопрос, я попытался реализовать рекурсивную функцию для вычисления
косинуса при помощи ряда Маклорена до указанной точности (эпсилон).

Формула:



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

Вот набросок функции без проверки точности:

import math

def cosine_rec(x, i=0, err=10**9, eps=1e-5):
    if i < 1:
        return 1
    # как проверить текущую ошибку ???
    res = (-1)**i * (x**(2*i) / math.factorial(2*i))
    return res + cosine_rec(x, i+1, err=err, eps=eps)


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


Ответы

Ответ 1



def cosine_rec(x, i=0, err=10**9, eps=1e-5): if i < 0: return 1 res = x**(2*i) / math.factorial(2*i) if res <= eps: return (-1)**i * res return (-1)**i * res + cosine_rec(x, i+1, err=err, eps=eps)

воскресенье, 29 марта 2020 г.

Вычислить координаты ортогональной проекции точки на отрезок

#javascript #математика #геометрия


Проект для создания чертежей в svg, на нативном js. 

Столкнулся со следующей задачей:

Известны координаты 3х точек A B C на плоскости. Точки A B являются началом и концом
отрезка AB. Нужно найти координаты ортогональной проекции (точка D) точки С на отрезок
АВ. Как это сделать?
    


Ответы

Ответ 1



Проекцию можно найти, используя скалярное произведение векторов D = A + AB * Dot(AC, AB) / Dot(AB, AB) В псевдокоде: abx = B.X - A.X aby = B.Y - A.Y dacab = (C.X - A.X) * abx + (C.Y - A.Y) * aby dab = abx * abx + aby * aby t = dacab / dab D.X = A.X + abx * t D.Y = A.Y + aby * t Здесь немного другие обозначения (C=P, N=D) и пояснения

Ответ 2



Метод "пересечения": Если построить точку С' = (Cx - (By - Ay), Cy + (Bx - Ax)), то прямая CC' будет перпендикулярна прямой AB. Пересечение прямых AB и CC' даст вам точку D. Метод "тяжеловат" с вычислительной точки зрения из-за того, что использует пересечение двух прямых в качестве примитива. Но прост в реализации, если такой примитив уже есть под руками. Метод "расстояния": Если AA, BB и CC - коэффициенты уравнения прямой, содержащей отрезок AB (легко вычисляются через координаты точек A и B), то величина DD = AA * Cx + BB * Cy + CC даст вам знаковое расстояние от этой прямой до точки C, домноженное на |(AA, BB)|. Если к точке C прибавить вектор DD * (-AA, -BB), то мы получим точку, которая смещена относительно C в правильном направлении, но, условно выражаясь, "слишком далеко": расстояние превышает требуемое в |(AA, BB)|^2 раз. Достаточно разделить смещение на эту величину - мы получим требуемую точку D DD D = C + ------------ * (-AA, -BB) |(AA, BB)|^2 Этот метод тоже несколько громоздок из-за "лишних" вычислений уравнения прямой, но может быть полезен, если вы и так уже знаете/вычисляете уравнение прямой, а также если вам в дополнение к точке проекции нужно еще вычислять и расстояние от C до AB. Метод "скалярного произведения": Очевидно, что |(A, D)| D = A + -------- * (A, B) |(A, B)| При этом скалярное произведение вектора (A, C) на вектор (A, B) - это длина проекции (A, D), домноженная на |(A, B)|. (A, C)•(A, B) = |(A, D)| * |(A, B)| Тогда (A, C)•(A, B) D = A + ------------- * (A, B) |(A, B)|^2 (Можно показать, что если собрать все вычисления второго метода в одну формулу, то она "сократится" до третьего метода.)

Как построить график разрывной функции?

#python #математика #matplotlib #графики


Я построил график функции путем создания двух прямых, не включающих в себя точку
разрыва: 

xList1 = np.arange(a, -5-h, h)
xList2 = np.arange(-5+h, b, h)

lineF1 = plt.plot(xList1, f(xList1), color='b', linewidth=2.0)
lineF2 = plt.plot(xList2, f(xList2), color='b', linewidth=2.0)


Для удобства, хотелось бы объединить xList1 и xList2, но в таком случае на графике
не образуется разрыв, а точки, которые должны были быть на границах разрыва, соединяются.
Есть ли способ избежать этого и построить график правильно?
    


Ответы

Ответ 1



для того чтобы получить разрыв достаточно заменить значения по X и Y - NaN (Not a Number): In [150]: x = np.linspace(-7, 7, 100) In [151]: x[(x>-1) & (x<2)] = np.nan In [152]: plt.plot(x, np.sin(x)) Out[152]: []

Как понять и решить задание нормализации числа с плавающей точкой?

#математика #числа_с_плавающей_точкой


Есть такое задание в книге Structured Computer Organization в Appendix B. Floating-point
numbers:


  4. The following binary floating-point numbers consist of a sign bit, an excess
64, radix 2 exponent, and a 16-bit fraction. Normalize them.
  
  a. 0 1000000 0001010100000001
  b. 0 0111111 0000001111111111
  c. 0 1000011 1000000000000000  


Мое решение

Я понимаю это задание так:

У нас есть следующий формат: 1 бит знака, 7 бит экспоненты, со смещением 64 и 16-битная
мантисса. 

Нам нужно нормализовать число 0 1000000 0001010100000001, представленное в этом формате.
Число называется нормализованным, если крайний левый бит его мантиссы — это 1. Значит
нам нужно сдвинуть все биты мантиссы влево на 3 бита. Если мы сдвигаем число влево
на три бита, значит умножаем его на 2³. Следовательно, чтобы число не изменилось, нам
нужно уменьшить экспоненту на 3.

Если исходное число (0 1000000 0001010100000001) в десятичный вид, то получим: 

+ 2^(64 - 64) × (2^(-4) + 2^(-6) + 2^(-8) + 2^(-16)) = +1 × 0.0820465087890625 =
+0.0820465087890625


Если перевести это же число, нормализованное мной, (0 0111101 1010100000001000),
то получим: 

+ 2^(61 - 64) × (2^(-1) + 2^(-3) + 2^(-5) + 2^(-13)) = +2^(-3) × 0.6563720703125
= 0.125 × 0.6563720703125 = +0.0820465087890625


Т. е. то же самое число.

Решение автора книги

Решения нашел здесь. Я так понимаю, это официальные ответы, представленные издательством.


  4. To normalize, shift left 1 bit at a time, adding 1 to the exponent at each step,
until the leftmost bit of the fraction is 1. The results are
  
  (a) 0 1000011 1010100000001000
  (b) 0 1000101 1111111111000000
  (c) 0 1000011 1000000000000000
  
  The third one is already normalized.


Решение @avp

Из-за расхождения в моем ответе и ответе автора, решил попросить помощи в чате C, C++:


  …я понимаю этот пример по другому. Задан знак 0 -- положительное, экспонента --
1000000 == 0x40 == 64 (т.е. с учетом смещения 64 это 0) и 16-разрядное целое 0x1501
== 5377.
  Вот его и надо переводить в дробь
  
  @eanmos вообще, ваш взгляд на задачу наверное более правильный (вычесть 3 из экспоненты).
В моей интерпретации сама формулировка задачи выглядит слишком вычурно. И ответ (ответов
в книге я не нашел) в моей интерпретации будет -- прибавить к экспоненте 12 (поскольку
5377 надо для перевода в дробь делить на 4096), что явно не совпадает с ответом о котором
вы спрашиваете.
  
  — @avp


Так какой ответ правильный? Как правильно решить это задание?
    


Ответы

Ответ 1



Вместо того, чтобы дублировать ответ из комментариев, немного распишу, как было бы представлено в IEEE 754 (Double precision) вышеупомянутое число 0.0820465087890625 В двоичном представлении без какого-либо дополнительного кодирования это число выглядит так: 0.00010101000000012 Что соответсвует нормализованному виду 1.0101000000012 × 2-4 тип double содержит такие битовые поля 1 бит - Знак 11 бит - Порядок (со смещением 1023) 52 бита - Мантисса (дробная ee часть) Целая часть мантиссы не записывается в память, дело в том, что числа в IEEE 754 (почти) всегда записываются в нормализованном виде, а значит перед точкой подразумевается единица. Итого получаем: Знак: 02 Порядок: -4 + 1023 = 011111110112 Мантисса: 01010000000100000000000000000000000000000000000000002 Проверить это можно, например, в python import struct value = 0b0_01111111011_0101000000010000000000000000000000000000000000000000 print(struct.unpack('d', struct.pack('q', value))) # (0.0820465087890625,) Так что единственная отличие от вашего решения в том, что порядок на единицу меньше.

Нахождение n-го члена последовательности. Проблема - бесконечный цикл [закрыт]

#cpp #c #алгоритм #математика


        
             
                
                    
                        
                            Закрыт. Этот вопрос не по теме. Ответы на него в данный
момент не принимаются.
                            
                        
                    
                
                            
                                
                
                        
                            
                        
                    
                        
                            Хотите улучшить этот вопрос? Переформулируйте вопрос,
чтобы он соответствовал тематике «Stack Overflow на русском».
                        
                        Закрыт 10 месяцев назад.
                                                                                
           
                
        
Цикл получается бесконечным, потому что |an-an-1| возрастает, а не убывает. В чем
проблема?



const double e = 0.0001;
int n = 0;
double an1;
double an = 0.0;

 do 
 {
    ++n;
    an1 = an;
    an = n*(sqrt(pow(n,2)+2*n)-2*(sqrt(pow(n,2)+n)+n));
} 

while (fabs(an - an1) >= e);

    


Ответы

Ответ 1



an = n * (sqrt(pow(n, 2) + 2 * n) - 2 * sqrt(pow(n, 2) + n) + n); Была лишняя скобка. Спасибо @Drawn Raccoon за ответ в комментариях!

воскресенье, 15 марта 2020 г.

Решение задач о рюкзаке методом ветвей и границ

#c_sharp #алгоритм #математика


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

void GetMaxSumAvailability(List ItemList, int i, int currentWeight, int currentCost)
{
    if(i <= ItemList.Count - 1)
    {
        // Если по весу проходит, то прибавляем стоимость и суммарный вес 
        if (currentWeight + ItemList[i].Weight <= width)      
        {
            ItemList[i].Use = true;
            GetMaxSumAvailability(ItemList, i + 1, currentWeight + ItemList[i].Weight,
currentCost + ItemList[i].Availability);
        }
        // Иначе не берем и смотрим следующий элемент
        else
        {
            ItemList[i].Use = false;
            GetMaxSumAvailability(ItemList, i + 1, currentWeight, currentCost);
        }
    }
    if (maxCost < currentCost && width >= currentWeight)
    {
        ItemList.ForEach(delegate(Item item) { if (item.Use) maxCost += item.Availability; });
        GetMaxSumAvailability(ItemList, i + 1, currentWeight, currentCost);
    }
}


Проблема заключается в неправильном выводе. К примеру, пусть width (вместительность
рюкзака) = 5. Есть три предмета:


Вес = 4; Ценность = 100
Вес = 2; Ценность = 60
Вес = 2; Ценность = 60


Тогда на выводе я получу 100, когда очевидно, что если взять 2 и 3 предмет, то суммарная
стоимость будет 120.

Однако, следующий пример выполняется правильно:
widht = 14


Вес = 5; Ценность = 3
Вес = 10; Ценность = 5
Вес = 6; Ценность = 3
Вес = 5; Ценность = 2


В итоге я получу 7, что подтверждает следующая пикча:

    


Ответы

Ответ 1



Уберите GetMaxSumAvailability в else - не надо подменять накопленные параметры текущими. Оставьте только ItemList[i++].Use = false; Увы, не всё так просто. В подобного рода задачах есть "жадная" стратегия, когда захватываются сначала более ценные предметы. Но данная реализация - точно не такая, поскольку набивает рюкзак первым, что туда может влезть. И обе эти реализации грешат отсутствием какого бы то ни было перебора. Если говорить о графике, то горизонтальная линия при подобном подходе отсутствует, а ограничения лишь маскируют этот факт. Чтобы эта линия возникала, надо рассматривать вариант с false даже в том случае, когда предмет влезает. Потому что в приведённом виде алгоритм не проверяет, что могло бы оказаться в рюкзаке вместо текущего предмета. И, таким образом, не рассматривает все варианты. В конечном итоге, все стратегии перебора ведут к одним и тем же допустимым наборам. Единственная внятная оптимизация состоит в той же "жадной" стратегии, позволяющей быстрее набить рюкзак и тем самым - отсечь недопустимые варианты. Метод ветвей и границ в данном случае ведёт только к тому, что рюкзак с вещами будет объявлен вершиной графа, а процесс набивания рюкзака - движением по его рёбрам. Итак, рекомендаций две: 1. Упорядочить предметы по весу и начинать перебор с более тяжёлых (жадная стратегия). 2. Рассматривать вариант ItemList.Use = false даже и в том случае, когда предмет влазит в рюкзак. Поскольку рекурсия в конце концов приведёт к оценке, то достаточно просто сравнивать эти два варианта при их наличии (бинарный перебор).

Как составить правильную формулу расчета угла между векторами?

#математика #геометрия #вектор


Есть стандартная формула расчета угла между векторами:



И вот есть два вектора, угол между которыми никак не вычислить, так как правая часть
уравнения меньше -1. Вот эти вектора:

var x1 = -0.045797169475341334, y1 = -0.9989507591808752;
var x2 = 0.04579716947534099, y2 = 0.9989507591808753;


В итоге, выражение:

(x1 * x2 + y1 * y2) / Math.sqrt(Math.pow(x1, 2) + Math.pow(y1, 2)) * Math.sqrt(Math.pow(x2,
2) + Math.pow(y2, 2))


дает результат: -1.0000000000000002

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

Как мне скорректировать формулу, чтобы этой ошибки не было?
    


Ответы

Ответ 1



Варианта решения проблемы имеются как минимум 2. Оба они требует, чтобы оба вектора имели ненулевую длину, но для вектора с нулевой длиной сама постановка вопроса о каком-либо угле не вполне корректна, и как Вам обрабатывать такую ситуацию, Вам должно быть виднее. 1) вычисляем промежуточное значение: r = (x1 * x2 + y1 * y2) / Math.sqrt(Math.pow(x1, 2) + Math.pow(y1, 2)) * Math.sqrt(Math.pow(x2, 2) + Math.pow(y2, 2)) Проверяем на попадание r в диапазон [-1, 1]: if (r < -1) r = -1; if (r > 1) r = 1; Вычисляем арккосинус. Так убираются ошибки округления, "выбивающие" r из диапазона. 2) Пользуемся функцией atan2 phi = Math.atan2(y2, x2) - Math.atan2(y1, x1); При необходимости, приводим полученный угол в нужный диапазон (0-180град или -90 - +90) Я бы написал так: if (phi < 0) phi += Math.PI * 2; if (phi > Math.Pi) phi -= Math.PI; Оба алгоритма выдадут ошибку при x1=y1=0 или x2=y2=0, о чем написано выше. Вариант с atan2, имхо предпочтительней, т.к., например при x1=y1=1e+10, x2=y2=1e-10 точность вычислений по первому варианту будет околонулевой (e10 взято просто для примера, м.б. нужно существенно больше). Математические функции указаны для Java, для .NET названия содержат заглавную букву: Math.Sqrt, Math.Atan2 и т. д.

Ответ 2



Если есть желание воспользоваться формулой для разности углов (а оно понятно из вопроса), то можно использовать тангенсы половинного аргумента с периодом 360: phi = 2(Math.atan2(y2, x2+r2) - Math.atan2(y1, x1+r1)); Проверим эту формулу на данных x1 = sqrt(3), y1=1, x2= - sqrt(3), y2=1 r1=r2=2: Правильный ответ: cos(fi) = -1/2, fi=120. По формуле для разности: fi1=2arctg(1/(2+sqrt(3))) = 30, fi2=2arctg(1/(2-sqrt(3))=150, fi=fi2-fi1 = 120. Остаётся только преобразовать интервал, на выбор: либо mod(fi,360), либо mod(fi+180,360)-180 В то же время: Модуль нужно убрать, потому что его нет в теории. Тангенсы в чистом виде лучше не использовать, потому что есть риск ошибки. Так, для тех же данных по формуле phi = (Math.atan2(y2, x2) - Math.atan2(y1, x1); имеем: fi = arctg(sqrt(-3)) - arctg(sqrt(3)) = -120. Парадокс объясняется тем, что тангенс имеет период 180, а сos(fi+180) = cos(fi)cos(180)+sin(fi)sin(180) = - cos(fi).

воскресенье, 8 марта 2020 г.

Вопрос по переводу чисел между системами счисления

#математика #информатика


Почему из десятичной в любую систему (что в двоичную, что в шестнадцатеричную) число
переводится методом деления, а из любой в десятичную - другим методом? Чем так обособлена
именно десятичная система? Ведь, с точки зрения математики она ничем не должна отличаться
от тех же двоичной и шестнадцатеричной кроме алфавита - набора символов. Мне всегда
казалось, что из большей в меньшую переводим методом деления, а из меньшей в большую
- другим методом. 
    


Ответы

Ответ 1



Если я правильно понимаю ваш вопрос, то, скорее всего, у вас когнитивный диссонанс :) из-за того, что вы выполняете перевод из двоичной в десятичную - ну, типа, 110101 = 1 + 4 + 16 + 32 = 53, и забываете о том, что вы уже работаете в десятичной записи. И на этом ваше преобразование завершено. Давайте иначе - через, ну, скажем, семеричную. Тогда 110101 = 1 + 4 + 22 + 44 = 104 Если бы вы работали в семеричной системе счисления, вы бы это 104 считали ответом и больше ничего не делали. Разве что задали бы здесь вопрос - и чем же семеричная система счисления такая выдающаяся? :) А теперь нужно 104 перевести в десятичную... Делим 104 на 13 (10 в семеричной), получаем 5, и в остатке 3, итого записываем в десятичной 53... Это ответ на ваш вопрос?

Ответ 2



Методы одинаковые и ничем не отличаются. 12345 dec = 30071 oct 12345 divrem 8 = 1543 1 1543 divrem 8 = 192 7 192 divrem 8 = 24 0 24 divrem 8 = 3 0 3 divrem 8 = 0 3 0o30071 divrem 10 = 0o2322 5 0o2322 divrem 10 = 0o173 4 0o173 divrem 10 = 0o14 3 0o14 divrem 10 = 0o1 2 0o1 divrem 10 = 0o0 1 Десятичная обусловлена исторически 10 пальцами на руках. А вообще, можно почитать :)

среда, 4 марта 2020 г.

Генерация сочетаний

#алгоритм #математика #комбинаторика


Необходимо построить сочетания всех возможных размеров так, чтобы элементы в сочетаниях
не совпадали (1,2,3; 1,3,2; 2,1,3; 2,3,1; 3,1,2; 3,2,1 - одни и те же сочетания)

Например, даны числа: 1,2,3,4

Ответ:

1
2
3
4

12
13
14
23
24
34

123
124
134

1234

Можно и псевдокод.
    


Ответы

Ответ 1



Вы забыли еще 234 и пустое сочетание :) Берем число, каждому биту которого соответствует один элемент вашего множества, и проходим все числа в цикле - от 00..00 до 11..11. Все. Каждое число - это одно "сочетание" (вообще-то это генерация всех подмножеств данного множества), которое строим, выбирая элементы, соответствующие единичным битам... Например, ваше 1234 - соответственно, 4-битное число... 0000 пустое множество 0001 1 0010 2 0011 12 0100 3 .... 1110 432 1111 4321

пятница, 28 февраля 2020 г.

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

#математика #геометрия


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

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


Ответы

Ответ 1



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

среда, 26 февраля 2020 г.

Как выводить результат с заданной пользователем точностью?

#java #математика #условия #точность


Условие: Написать программу, вычисляющую число   с точностью, задаваемой пользователем.
Известно, что сумма ряда 1-1/3+1/5-1/7+1/9-…приближается к значению  pi/4 при достаточно
большом количестве членов ряда.

public class Class {
    public static void main(String[] args) {
        double a = 3;
        double sum=1;
        for (int i=0;i<1000;i++){

            if (i%2==0){
                sum=sum-1/a;
            }
            if (i%2!=0){
                sum=sum+1/a;
            }
            a+=2;
        }
        double pi=sum*4;
        System.out.println(pi);

    }
}


Само pi я рассчитал, но как задать количество знаков после запятой? 
    


Ответы

Ответ 1



Можно вот так: String.format("%.2f", value); Взято из: https://stackoverflow.com/a/1276096/2871225

Ответ 2



От вас требуется не вывести число с определенным количеством знаков, а посчитать с определенной точностью. Ваш алгоритм делает фиксированное количество шагов for (int i=0;i<1000;i++) А должен останавливаться при достижении нужной точности, например так while (Math.abs(1 / a) > e) где e - введенное пользователем число, например 0.0001

Почему при проверке на простоту перебирают значения только до квадратного корня?

#алгоритм #математика


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

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


Ответы

Ответ 1



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

Символьные вычисления (упростить математическое выражение)

#python #математика


Есть вот такой пример:

0.88522+(x-0.2)*0.9823456+(x-0.235)(x-0.2145)*(-0.976123)


Необходимо преобразовать его к следующему виду (результат из Wolfram Mathematica):

0.639547 + 1.42111 x - 0.976123 x^2


Как можно это реализовать в python?
    


Ответы

Ответ 1



Воспользуйтесь модулем Sympy: from sympy import simplify from sympy.parsing.sympy_parser import ( parse_expr, standard_transformations, implicit_application, implicit_multiplication, implicit_multiplication_application, function_exponentiation) transformations=(standard_transformations + (implicit_multiplication, implicit_application, function_exponentiation, )) formula = "0.88522+(x-0.2)0.9823456+(x-0.235)(x-0.2145)(-0.976123)" expr = parse_expr(formula,transformations=transformations) simplified_formula = simplify(expr) print(simplified_formula) результат: -0.976123*x**2 + 1.4211128885*x + 0.6395469598775 PS установка Sympy

вторник, 25 февраля 2020 г.

Как составить предложение из букв, введенных пользователем?

#php #алгоритм #математика #поиск


На вход программе подаётся набор английских букв. Имеется словарь из слов.

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

Пример

Входящий набор символов: hellomyfriend

Вывод программы:

Feed Hilly Morn
Feed Hilly Norm
Feed Horny Mill
Freehold Nil My
Refilled Hon My
Defiler Hymn Lo
и т.д.




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

Я делал методом перебора всех слов. Для фразы myfavoritegame скрипт отрабатывал около
5 минут. На сайте предложения отдаются мгновенно.

Можете что-нибудь посоветовать?
    


Ответы

Ответ 1



Предположим, вам нужно просто найти все возможные комбинации слов из данного словаря, удовлетворяющие условию. Не отвлекаясь на особенности именно языка. Важно для поиска: наличие букв в слове. Исключаем содержащие буквы вне заданных. Исключаем, по мере составления фразы, уже использованные. считаем буквы. В любой момент составления фразы известно, слова какой длины подходят, или точно не подходят. повторы букв. Не важно: порядок букв в словарном слове. смысл слова. Для скорости нужно сделать поиск по важным признакам максимально быстрым, при необходимости убрав неважные аспекты. Для быстрого нахождения слов с подходящим набором букв, но без учёта повторов, можно сделать битный индекс, как предложил @Mirdin: 26 букв английского алфавита = 26 бит. Пронумеровать слова в словаре (или просто индексом считать номер строки) и составить отдельный индекс в две колонки: id слова - битовая маска имеющихся букв. Для словаря меньше 65 тыс. слов, такой индекс будет "весить" 6 байт на слово, менее 400k. Можно держать в оперативной памяти для почти-мгновенного поиска. Так можно быстро найти например, первое слово фразы – просто, чтобы точно были выключены биты "лишних" букв. Стоит сделать копию словаря, где буквы слова отсортированы по алфавиту, и слова отсортированы по алфавиту. Т.е. опять отдельный индекс: сортированные_буквы - id_слова. Этот индекс будет тяжелее самого словаря на (число слов * 2 или 3 байта). В этом индексе можно быстро находить подходящие слова и отбрасывать точно-неподходящие. Алгоритм примерно такой. Ищем первое слово. Хочется найти первое же слово наибольшей возможной длины. Перебор по длине, от большего к меньшему. Ест допустимый набор букв и длина. Нашли первое слово, обновили допустимый набор букв и длины слов – ищем следующее слово.

Ответ 2



Делаете структуру (таблицу в БД, хеш-таблица, словарь и тд что там есть в пхп) из двух полей на запись: хэш и собственно говоря слово. Забиваем эту структуру словами. Хэш примерно делается так: индекс буквы в алфавите - степень двойки, все буквы слова складываем (32 бит для русского языка должно хватить). Это простейший вариант, возможно будет необходимо придумать более сложную функцию. Получив слово которое надо заанаграмить - вычисляем его хэш и ищем по нему в нашей структуре UPD: Это для одного слова, с фразами будет сложнее, но принцип тот же

Заполнить клетки прямоугольника 2*100 непрерывной последовательностью чисел

#алгоритм #математика


В тетрадке нарисован прямоугольник 2×100. Требуется записать в его клетки числа от
1 до 200 так, что любые два числа, отличающиеся на единицу были записаны в клетки,
соседние по стороне. Сколько существует способов это сделать?
    


Ответы

Ответ 1



Лемма Если зафиксировать столбцы, на которых находятся числа 1 и 200, то существует ровно 2 варианта решения если столбцы различные, существует ровно 2 варианта решения если это один и тот же столбец и он - крайний, в остальных случаях решения не существует. Доказательство очевидно (т.е. мне лень его писать). Решение Всего 100*100 вариантов выбора столбцов для чисел 1 и 200, из них 98 - невозможные. Итого - 2*(100*100 - 98) = 19804 варианта. Где я ошибся? :)

Ответ 2



Попробуем решить задачу рекурсивно. Пусть F(n) — количество решений для доски 2 × n, G(n) — количество решений для доски 2 × n, которые начинаются в левом нижнем углу. (Мы предполагаем, что прямоугольник расположен горизонтально). Не ограничивая общности, предположим, что первая точка расположена внизу (это уменьшает количество вариантов для F(n) вдвое). Очевидно, в каждом из путей должен быть вертикальный отрезок. Рассмотрим первый из них. Пусть он будет на k-ом шагу. Наш путь имеет вид k+1 1 2 3 ... k У нас есть такие варианты: 1) k ≠ 1, и верхняя часть идёт в том же направлении, что и первый отрезок: k+1 k+2 1 2 3 ... k-1 k В левую часть от k + 1 попасть невозможно, и эта часть непуста. Итак, этот вариант невозможен. 2) k ≠ 1, и верхняя часть идёт в противоположном от первого отрезка направлении. k+2 k+1 1 2 3 ... k-1 k при этом наш путь не может попасть в правую часть, то есть, она должна быть пустая. То есть путь выглядит так: 2k 2k-1 2k-2 ... k+2 k+1| 1 2 3 ... k-1 k | Остаток доски имеет размеры 2 × (n − k), и её можно обойти G(n − k) способами. 3) k = 1 2 3 1 при этом наш путь не может попасть в левую часть, значит, она должна быть пуста, то есть имеем такую картинку: |2 3 |1 Количество вариантов обхода равно G(n − 1). Составим рекуррентные соотношения. Для произвольной начальной точки: Количество вариантов в 1) равно 0. Количество вариантов в 2) равно сумме по всем возможным k (от 2 до n) величин G(n − k), т. е., G(n − 2) + G(n − 3) + ... + G(n − n) = G(0) + G(1) + ... + G(n − 2). Эту сумму надо ещё умножить на 2, для двух возможных направлений (влево и вправо), и ещё на 2 для двух возможных строк для начальной точки. Количество вариантов для 3) равно G(n − 1), и это надо умножить на 2 для двух возможных строк для начальной точки, и ещё на 2 для положения единицы слева и справа. Итого: G(n) = 4 × [G(0) + G(1) + ... + G(n − 1)] Теперь составим рекуррентное соотношение для G(n). Рассмотрим те же три варианта (помним, что 1 находится в левом нижем углу). Вариант 1) невозможен по тем же причинам. Вариант 2) по сути означает такую картинку |2k 2k-1 2k-2 ... k+2 k+1| | 1 2 3 ... k-1 k | то есть k = n, это даёт ровно один вариант при n > 1 и ни одного варианта при n = 1. Вариант 3) остаётся неизменным и даёт G(n − 1) вариант. Итого: G(0) = 1 G(1) = 1 G(n) = 1 + G(n − 1) для n > 1 Отсюда легко выходит, что G(n) = n при n > 0. Окончательно получаем: F(100) = 4 × ([1 + 1 + 2 + 3 + ... + 99]) = 4 + 4 × 99 × 100 / 2 = 19804. Надеюсь, что нигде не ошибся.

Ответ 3



Лемма 1. Фрагмент пути может быть только двух видов: змейка или петля. Змейка – непрерывная цепочка вертикальных П-образных фрагментов, где чередуется их вертикальная направленность. Петля – горизонтальный П-образный фрагмент, где ножки могут быть любой (и разной) длины. Лемма 2. Петля хотя бы с одной стороны подходит к краю. Лемма 3. Петель может быть 0, 1 или 2. Лемма 4. Змеек может быть 0 или 1. Наличие Змейки разрывает концы Пути (0 и 200). При отсутствии змейки концы соседствуют. Минимальной длиной Петли по горизонтали считаем 1 переход (2 значения): 1 2 4 3 Минимальной длиной Змейки по горизонтали считаем 2 перехода (3 значение по гор.: 1 2 3 4 Сосчитаем варианты: Петли Змейка Варианты 0 0 0 – нет такого варианта 0 1 4 - из каждого из углов стартует змейка по вертикали 1 0 200 * 2 - все варианты одной петли 1 1 4 * ? - про этот случай дальше. 2 1 4 * ? - про этот случай дальше: Петля и змейка. Минимальная длина такой штуки - 4. 96 остаётся для распределения. Сколькими способами можно составить 96 из двух натуральных слагаемых, считая нули? 0+96, 1+95 .. 95+1, 96+0 = 97 вариантов. Не забыть домножить на 2 (порядок змейки и петли), и на 4, т.к. симметрия. +776 В случае, когда есть 2 петли и змейка, минимальная длина такой конструкции 5: ПZП. Остаётся 95 позиций для распределения среди трёх элементов. Задача сводится к кол-ву композиций 95 из трёх слагаемых, считая нули, и учитывая порядок. Формула для кол-ва композиций числа n из k слагаемых, считая нули: ( n + k - 1 ) скобки высотой две строки ( k - 1 ) Пусть верхняя часть N = n + k - 1, а нижняя M = k - 1. Вычисляется как N! ----------- K! * (N-K)! Для 95 и 3 у меня получилось 4656 и домножить на 4. +18624 Петли Змейка Варианты 0 0 0 0 1 4 1 0 400 1 1 776 2 1 18624 -------------------- 19804 Результат: 19804. А теперь выкладывайте простой, как двоичные числа, тривиальный вариант решения, который наверняка есть. ) Upd. глядя на два независимых других результата @pavel-mayorov и @vladd, исправил ошибку у себя – не учитывал сначала порядок змейки-петли в варианте 1:1.

Ответ 4



Как-то так (не проверял): Поместить единицу в левую верхнюю клетку. Вараинты записи есть либо змейкой, либо, начало змейкой, а потом по прямой до конца и обратно во втором ряду. Думаю, дла этого можно составить формулу. Из соображений симметрии, умнодить ответ на 4. Ещё умножить на 2 и вычесть 4 - это то, что началось со змейки, а кончилось невырожденным кольцом, если идти по нему в обратную сторону. Добавить 2*196 как кольца в двух направлениях с любой клетки, кроме угловых. Данный ответ некорректен, не учтен варианты такого плана: 2 1 6 7 8 3 4 5 10 9 Спасибо @PavelMayorov за нахождение этого случая. Добавить 2*96 конструкций приведённого выше вида.

Вычисление пропорций цветов

#алгоритм #математика


Дано:
 4 RGB цвета - A,B,C,D
 Цвет D получен в результате смешивания A,B,C.

Вопрос: 
 как получить пропорцию выбранного нами цвета(например А) в результирующем D?

Моя попытка решить:


Преобразуем A,B,C,D в CIELAB для использования результата в виде Vector3.
Строим треугольник где A,B,C вершины, D - центр масс(предположительно)
Из D строим перпендикуляры к AB и AC и получаем четырехугольник, который представляет
собой площадь, которая является пропорцией искомого цвета.(предположительно)
Считаем площадь треугольника, и вычисляем отношение с результатом из пункта 3.


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

PS: CIELAB вполне ок, в задаче с 2-мя цветами результат отвечает ожиданиям.

Для лучшего понимания проблемы прикладываю пикчи:

Это слой, который содержит так называемые цветовые идентификаторы объекта(colorId),
этим цветом заливается объект целиком.


Этот слой содержит сглаживание:



Нужно получить:


Поясню почему так. В данном случае объекты которые имеют зеленый и коричневый colorId
выбраны, и их нужно отобразить. Причем мне нужно выделить их так на маске, что бы граница
не была резкой. Для вычисления границ используются данные из слоев colorId и colorId
+ AA. В случаях когда рядом с пикселем граничат лишь 2 colorId, проблем с расчетом
пропорций нет. Проблема когда 2+.

Варианты с блюрами\и различными сглаживаниями не катят. Нужно повторить 1 в 1 то
что изображено на 3-й картинке, т.к. данный продукт используется в графике.
    


Ответы

Ответ 1



А почему бы не взять просто средневзвешенное? result.r = (A.R * weight_A + B.R * weight_B + C.R * weight_C) / (weight_A + weight_B + weight_C); и т. д. По результатам дискуссии в чате, выяснилось следующее. При сглаживании цветов цвет резцльтирующего пикселя будет (приближённо) оцениваться как средневзвешенное цветов исходных пикселей с некоторыми весовыми коэффициентами. Задача состоит в том, чтобы найти эти самые весовые коэффициенты. Причём под значением пикселя подразумевается его координаты в цветовом пространстве LAB (как ни странно, это имеет определённый физический смысл). Итак, вычисление. Пусть компоненты цветов A, B, C в цветовой модели LAB равны (x₁, y₁, z₁), (x₂, y₂, z₂), (x₃, y₃, z₃) соответственно, а компоненты результирующего цвета D — (x, y, z). Пусть неизвестные весовые коэффициенты равны α, β, γ. Имеем: α ⋅ (x₁, y₁, z₁) + β ⋅ (x₂, y₂, z₂) + γ ⋅ (x₃, y₃, z₃) = (x, y, z) Поскольку суммарный вес должен равняться 1, то α + β + γ = 1. Получаем систему α ⋅ x₁ + β ⋅ x₂ + γ ⋅ x₃ = x α ⋅ y₁ + β ⋅ y₂ + γ ⋅ y₃ = y α ⋅ z₁ + β ⋅ z₂ + γ ⋅ z₃ = z α + β + γ = 1 Поскольку точки должны лежать в одной плоскости, третье уравнение обязано быть следствием первых двух, убираем его. Заменяем γ = 1 − α − β, получаем: α ⋅ x₁ + β ⋅ x₂ + (1 − α − β) ⋅ x₃ = x α ⋅ y₁ + β ⋅ y₂ + (1 − α − β) ⋅ y₃ = y или α ⋅ (x₁ − x₃) + β ⋅ (x₂ − x₃) = x − x₃ α ⋅ (y₁ − y₃) + β ⋅ (y₂ − y₃) = y − y₃ Это система линейных уравнений с двумя неизвестными, решаем её стандартным образом.

понедельник, 24 февраля 2020 г.

Раскрыть скобки (a+b)%c

#математика #логика


Можно ли преобразовать (a+b)%c к сумме слагаемых, каждое из которых не содержит a
и b одновременно?
    


Ответы

Ответ 1



Операция % дистрибутивна (a + b)%c = (a%c + b%c)%c

Ответ 2



Можно в случае a + b < cтогда подходят a и b по отдельности. Иначе - нельзя.

Ответ 3



На самом деле мне нужно выразить i-тый бит суммы чисел (a+b). ((a+b)>>i)&1 ((a+b)/pow(2,i))%2

Повторение результата при возведении в степень

#python #алгоритм #python_3x #математика


Как мне вычислить все возведения в степень с x_min ^ y_min по x_max ^ y_max без повторов
результатов. Например: 


x2 ^ x8 = z
...
x85 ^ y23 = p
...
но x85 ^ y28 = z !


Как видно, результат z повторяется у разных вычислений, каким образом можно  пропускать
все следующие вычисления при условии, что сравнивать с ранее вычисленными результатами
не допустимо?

# code python 3
min_x, max_x = 5, 127
min_y, max_y = 2, 250
result = ''
for x in range(min_x, max_x):
    for y in range(min_y, max_y):
        result += str(x ** y)

    


Ответы

Ответ 1



UPDATE: - только что проверил с вашими числами - Python "занимал" около 1GB памяти, timing я обновил для ваших чисел In [105]: x = np.arange(5, 8800, dtype=np.uint64) In [106]: y = np.arange(2, 8250, dtype=np.uint64) In [107]: r = np.unique(np.array([np.power(x, np.repeat([pw], len(x))) for pw in np.nditer(y)])) In [108]: len(''.join(r.astype(str))) Out[108]: 457656 In [109]: %timeit np.unique(np.array([np.power(x, np.repeat([pw], len(x))) for pw in np.nditer(y)])) 1 loop, best of 3: 11.3 s per loop In [115]: np.version.version Out[115]: '1.10.4' numpy solution: x = np.arange(5, 127, dtype=np.uint64) y = np.arange(2, 250, dtype=np.uint64) r = np.unique(np.array([np.power(x, np.repeat([pw], len(x))) for pw in np.nditer(y)])) # чтобы сэкономить память записываем строку в `r` r = ''.join(r.astype(str)) print(r) Timing для 5000 x 5000 массива: In [102]: %timeit np.unique(np.array([np.power(x, np.repeat([pw], len(x))) for pw in np.nditer(y)])) 1 loop, best of 3: 3.89 s per loop In [103]: x.shape Out[103]: (4995,) In [104]: y.shape Out[104]: (4998,)

Ответ 2



Так как основная проблема автора - недостаток ресурсов (или все-таки какая-то абстрактная задачка?), то можно предложить такой выход из положения: не генерировать сразу все-все значения, а написать генератор, который будет выдавать значения тогда, когда это будет нужно. Эти значения ведь все равно где-то будут использованы - будь то запись в файл или какие-нибудь операции над получившимся вектором - и использованы они будут по-одному. def give_me_square(): first = range(5, 8800) second = range(2, 9000) for i in first: for j in second: yield i**j counter = 0 total = 8800*9000 for square in give_me_square(): if counter % 10000 == 0: print(counter, "of", total) counter += 1 Работает совсем нешустро, зато память не ест. Такой генератор можно сделать асинхронным, можно отдать его в multiprocessing.Pool.map, много чего можно придумать.