Решил написать простую программу в целях обучения и практики. Решение правильное, но очень медленное. Сдаю задачку на сайте, 13 из 27 тестов выдаёт около 2 секунд, а нужно меньше 1 секунды. Постарался максимально сократить все if-ы, которые только возможно было, но и это не помогает. Думаю, что основное время затрачивается на прокрутку цикла, но иначе задачу не решить же.
У других пользователей среднее время в этой задачке от 14 до 100 мс. Я просто недавно начал, и может этот алгоритм который я написал совсем никак не ускорить, но по другому я просто не представляю как можно это решить. условие задачи Новый русский Витек приватизировал участок в Междолине размером m квадратов с севера на юг и n квадратов с запада на восток.
Он решил построить в пределах этого участка дом размером a квадратов с севера на юг и b – с запада на восток. Некоторые квадраты радиоактивны, и Витек не хочет на них строить дом. Кроме того, Витек хочет, чтобы расстояния от стен до границ участка выражалась целым числом квадратов. Долго выбирал он место для дома, но так и не выбрал – слишком много вариантов. А сколько?
Как увеличить время работы iPhone
Начал наш герой считать, но не сумел – плохо математику учил. Помогите ему. Входные данные Напишите программу, которая считывает числа m, n, a, b, k (1 ≤ a ≤ m ≤ 5000, 1 ≤ b ≤ n ≤ 5000, 0 ≤ k ≤ m * n), где m, n – размеры участка, a и b – размеры дома, k – количество радиоактивных квадратов, а затем k неповторяющихся пар чисел i и j (1 ≤ i ≤ m, 1 ≤ j ≤ n), которые определяют координаты радиоактивных квадратов. Выходные данные Вывести искомое количество способов расположения дома. На С++
#include using namespace std; int main() < int m, n, a, b, k, S, Ox, Oy, Oxmain = 1, Oymain = 1, l, t; // m висота n ширина; a висота b ширина bool y = false; cin >> m >> n >> a >> b >> k; int i[100000]; for (l = 1; l > i[l] >> i[l + 1]; > S = a * b; l = 0; while (Oymain > > Oy = Oymain; > if (y); else l++; y = false; Oxmain++; > Oxmain = 1; Oymain++; > cout
Отслеживать
задан 9 окт 2020 в 19:39
user410415 user410415
У вас матрешка из 5-ти вложенных циклов, хотя вложенность 2-й степени циклов уже дает O(n^2) .
9 окт 2020 в 19:55
Матрица m * n заполнена нулями, к точкам с данными индексами, присваиваются единицы. Теперь нужно считать количество площадей, размером a * b, где не встречаются элементы с ненулевым значением
Источник: ru.stackoverflow.com
Отключение ВСЕХ ненужных служб в Windows 10 | Оптимизация Windows 10
Как сократить время работы программы
: 167
Как сократить время работы программы
Даётся число n. Нужно вывести количество пар чисел, сумма которых будет равна числу n, и сумма цифр которых будет равняться сумме цифр числа n.
Например, число 11.
Пары, которые подходят:
11 0
0 11
1 10
10 1.
Значит, ответ 4, т.к. 4 пары.
Я делал так:
делаю цикл от 1 до n, и перебираю варианты: 0 и n, 1 и n-1, 2 и n-2, и т.д.
Записываю числа в строковую, потом каждый символ превращаю в цифру и помещаю в массив. В массиве подсчитываю сумму элементов.
Проверяю, sum=sum1+sum2 или нет.
И считаю кол-во вариантов.
Но число может быть от 1 до 10^100.
И при больших числах слишком долго выполняется цикл, а потом и в longint не влезает.
Подскажите, как еще можно реализовать это задание?
var a,a1,a2:array[1..100]of byte; 3 массива, первый для n, второй и третий нужны при перебирании чисел fi,fo:text; i,n,n1,n2,k,sum,i1,i2,l,sum1,sum2:longint; x,y,z:longint; s,s1,s2:string; begin Assign(fi,’sequence.in’); Assign(fo,’sequence.out’); Reset(fi); Rewrite(fo); Read(fi,x);=читает число n (у меня x) Str(x,s);преобразуем его в строку n:=length(s); l:=0; for i:=1 to 100 do a[i]:=0; элементы первого массива обнуляем, на всякий случай for i:=1 to n do val(copy(s,i,1),a[i],k); число x по цифрам кидаем в массив for i:=1 to n do sum:=sum+a[i];считаем сумму цифр числа x y:=0; z:=x; for i:=1 to x do begin вот этот цикл слишком долго идет при больших числах for i1:=1 to 100 do a1[i1]:=0; for i2:=1 to 100 do a2[i2]:=0;обнуляем массив 2 и 3, т.к. при новом проходе через цикл они буду заполнены Inc(y); Dec(z); sum1:=0; sum2:=0; Str(y,s1); n1:=length(s1); Str(z,s2); n2:=length(s2); for i1:=1 to n1 do val(copy(s1,i1,1),a1[i1],k); for i2:=1 to n2 do val(copy(s2,i2,1),a2[i2],k); for i1:=1 to n1 do sum1:=sum1+a1[i1]; for i2:=1 to n2 do sum2:=sum2+a2[i2]; считаем сумму цифр первого и второго числа для проверки if (sum1+sum2=sum)then begin проверяем, подходит ли условие Inc(l);наращиваем счетчик end; if (sum1+sum2=sum)and(y=z) это на случай, к примеру, для 20. То есть бывает 0 20 и 20 0, две пары. А с 10 10 и 10 10 аналогично, просто не видно, что их меняют местами then Inc(l); end; Inc(l); Программа посчитает x 0, а 0 x не посчитает. Write(fo,l); Close(fo); end.
Последний раз редактировалось dimon_snake; 30.01.2016 в 22:05 .
Источник: www.programmersforum.ru
Оптимизация времени выполнения программы на С++ (убираем условные переходы)
При оптимизации времени выполнения алгоритма, использующего LDPC декодер, профайлер привел к функции, вычисляющей следующее значение:
где a и b — целые числа. Количество вызовов шло на миллионы, а реализация ее была достаточно проста и бесхитростна:
int LLR(int a, int b) < if (a>0) return (b>0) ? __min(a,b) : -__min(a,-b); else return (b>0) ? -__min(-a,b) : __min(-a,-b); >
Функция состоит, по сути, из трех последовательных операций сравнения. Это дает (с учетом оптимизации компилятора) два (если числа разных знаков) или три (если одного) условных перехода для получения результата. Вспомнив о потенциальных проблемах конвеера при большом количестве условных переходов, было решено уменьшить их количество или даже избавиться от них. Для оценки быстродействия был написан небольшой
тестовый проект
#include static inline int LLR(int a, int b) < if (a>0) return (b>0) ? __min(a,b) : -__min(a,-b); else return (b>0) ? -__min(-a,b) : __min(-a,-b); > int _tmain(int argc, _TCHAR* argv[]) < SetPriorityClass(GetCurrentProcess(),REALTIME_PRIORITY_CLASS); SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_TIME_CRITICAL); srand(0); int x(0); __int64 t1,t2; QueryPerformanceCounter((LARGE_INTEGER*) for (size_t i=0;i>4;i++) < int a = rand() — RAND_MAX / 2; int b = rand() — RAND_MAX / 2; /* x += LLR(a,b);*/ >QueryPerformanceCounter((LARGE_INTEGER*) t2 -= t1; QueryPerformanceFrequency((LARGE_INTEGER*) _tprintf_s(_T(«%f»),t2/(t1*1.)); return 0; >
Сборка производилась в MSVS 2008 в конфигурации Release (настройки по умолчанию) для платформы x86.
Для начала, закомментируем вызов расчетной функции чтобы оценить время выполнения цикла с генерацией случайных чисел (к сожалению, вызов QueryPerformanceCounter() сам по себе довольно медленный и приводит к значительному искажению результата если его делать внутри цикла). Запускаем проект несколько раз, чтобы убедится в повторяемости результата. На машине с процессором Intel Core i7-3770 c частотой 3.4 ГГц время выполнения составляет в среднем 9.2 секунды. Если раскомментировать вызов расчетной функции, пересобрать проект и повторить эксперимент — получаем примерно 11.5 секунд. Прирост времени выполнения налицо, с ним и будем бороться.
Попробуем избавиться от условных операторов. Для начала выясним знак произведения a и b. Вычислять его в лоб некорректно из-за возможного переполнения. Так как нам важен только знак (то есть значение бита знакового разряда целого числа), то уместно воспользоваться операцией xor для определения знака произведения a и b. Далее, сдвинем результат a xor b вправо на 31 бит (оставляя только знаковый разряд) и получим 0 в случае неотрицательного числа и 1 в случае отрицательного. Умножим это значение на два, вычтем из единицы и получим -1 для отрицательного числа и 1 для неотрицательного:
int sign = 1-2*((unsigned)a^b)>>31);
Теперь рассчитаем модули a и b. Аналогичным методом определяем знаковые коэффициенты a и b и умножаем на них:
int c; c = 1-2*(((unsigned)a)>>31); a *= c; c = 1-2*(((unsigned)b)>>31); b *= c;
Перейдем к расчету минимума. Поскольку a и b уже имеют неотрицательные значения, рассчитать минимум можно, ориентируясь на знак их разности:
int numbers[2], min; numbers[0] = b; numbers[1] = a; a -= b; c = (unsigned(a))>>31; min = numbers[c];
Соберем все вместе и получим
следующую функцию
static inline int LLR_2(int a, int b) < int sign, numbers[2]; sign = 1-2*(((unsigned)a^b)>>31); a *= 1-2*(((unsigned)a)>>31); b *= 1-2*(((unsigned)b)>>31); numbers[0] = b; numbers[1] = a; a -= b; return sign*numbers[((unsigned)a)>>31]; >
Заменим вызов LLR() на LLR_2() и посмотрим, что получилось.
В ассемблерном коде теперь ни одного условного перехода, зато появились три инструкции целочисленного умножения imul (умножение на 2 компилятор сам заменяет на сдвиг). Прогнав тестовую программу, получаем следующий результат — время выполнения составляет 9.4 секунды! Итого, сравнивая времена «нетто» (2.1 и 0.2 секунды соответственно) для двух вариантов расчета искомого значения, получаем десятикратное * увеличение скорости выполнения требуемой операции.
Попробуем пойти еще немного дальше. Целочисленное умножение необходимо только для перемены знака числа, которую можно выполнить напрямую. В частности, вычисление модуля числа a можно реализовать следующим образом:
unsigned int mask[] = ; unsigned int constant[] = ; int c = ((unsigned)a)>>31; a = a^mask[c]+constant[c];
И наконец, заменим сдвиг вправо на 31 бит единичным циклическим сдвигом влево с помощью функции _rotl(). Забегая вперед отметим, что компилятор преобразует ее вызов напрямую в инструкцию rol без использования call. Соберем все вместе еще раз и получим
третий вариант
static unsigned int mask[] = ; static unsigned int constant[] = ; static inline int LLR_3(int a, int b)
Заменяем вызов LLR()_2 на LLR_3() и видим, что значимого прироста это не дает (время выполнения составляет примерно те же 9.4 секунды с небольшой разницей в третьем знаке в меньшую сторону). Получается, что imul на современных процессорах выполняется довольно быстро!
Вернемся к алгоритму, с которого все началось. Описанной оптимизацией только лишь одной функции удалось сократить время обработки единичного блока данных со 160 до 140 секунд (при выполнении на i7), что составляет более 10% и является весьма неплохим результатом. На очереди еще несколько похожих функций…
Ну и напоследок предлагаю вариант реализации функции определения максимума двух целых 32-разряздных чисел. Написан он был уже чисто из академического любопытства. Желающие могут не спешить заглядывать под спойлер и попробовать реализовать подобное сами. Вариант без условных переходов работает примерно втрое быстрей стандартного макроса __max() (при выполнении на i7). Удачи в оптимизации ваших программ!
Скрытый текст
static inline int max(int a, int b) < int numbers[2][2]; numbers[0][0] = b; numbers[0][1] = a; numbers[1][0] = a; numbers[1][1] = b; int sign_a = (unsigned)a>>31; int sign_b = (unsigned)b>>31; int sign_ab = (unsigned)(a^b)>>31; int abs_a = (1-2*sign_a)*a; int abs_b = (1-2*sign_b)*b; int sign_a_b = (unsigned)(abs_a-abs_b)>>31; int c0 = sign_ab; int c1 = ((1^(sign_a_b)^(sign_a | sign_b)) sign_a); int result = numbers[c0][c1]; return result; >
*UPD. В комментариях пользователь shodan привел пример, в котором rand() убран из подсчета времени, а чтение данных производится из заранее сформированного массива. В этом случае прирост производительности приблизительно трехкратный. По-видимому, это связано с более эффективной работой конвеера при чтении данных из массива.
- программирование
- с++
- алгоритмы
- оптимизация программ
Источник: habr.com