Пришлось тут на днях решать для студента (первого курса) задачку.
Дословно выглядит вот так:
2014 ACM-ICPC China Hunan Invitational Programming Contest
There is a simple problem. Given a number N.
You are going to calculate N%1+ N%2+ N%3+...+ N%N.
Input: The length N(1<=N<=10^12).
Output: Answer.
Sample input 5
Sample output 4
Вроде ничего сложного, однако-ж результат суммирования по модулю явно не уложится ни в один из поддерживаемых типов (int64 максимум 8 байт).
Сразу уточню: задачка олимпиадная, причем свежего 14-го года :)
Конечно странно что такие вещи задают первокурснику, но...
В итоге для обкатки накидал такое решение на двух полусумматорах - банальная битовая арифметика:
#include "stdafx.h" // хранилище для оооочень большого числа unsigned char UInt128[16]; void printUInt128(){ printf("result = 0x"); for (int i = 15; i >= 0; i--) printf("%hhX", UInt128[i]); printf("\n"); } // полный битовый сумматор реализующий таблицу истинности: http://ivatv.narod.ru/zifrovaja_texnika/1_04.htm bool fullAdder(bool a, bool b, bool p0, bool *p){ // первый полусумматор bool bRes = false; *p = false; if ((a && !b) || (b && !a)) bRes = true; if (a && b) *p = true; if (!p0) return bRes; // второй полусумматор *p = true; bRes = !bRes; if (!a && !b) *p = false; return bRes; } // сумматор на битовых операциях void add(long long x){ bool pFlag = false; unsigned long long halfResult = 0; unsigned long long bigValue; int i; bool aBit, bBit, sFlag; // получаем указатель на массив, содержащий большое число unsigned long long *p; p = (unsigned long long*)&UInt128[0]; // берем младшие 8 байт bigValue = (unsigned long long)(*p); for (i = 0; i < 64; i++){ // и побитно, посредством полного сумматора складываем два числа aBit = ((unsigned long long)1 << i & x) > 0; bBit = ((unsigned long long)1 << i & bigValue) > 0; sFlag = fullAdder(aBit, bBit, pFlag, &pFlag); if (sFlag) halfResult |= (unsigned long long)1 << i; }; // результат помещаем обратно *p = halfResult; // если нет переноса бит от предыдущей операции, то можно выходить if (!pFlag) return; halfResult = 0; // сдвигаемся на 8 байт p++; // берем старшие 8 байт bigValue = (unsigned long long)(*p); for (i = 0; i < 64; i++){ // увеличиваем значение опираясь на бит переноса bBit = ((unsigned long long)1 << i & bigValue) > 0; sFlag = fullAdder(false, bBit, pFlag, &pFlag); if (sFlag) halfResult |= (unsigned long long)1 << i; }; // результат помещаем обратно *p = halfResult; } int _tmain(int argc, _TCHAR* argv[]) { unsigned long long a, i; printf("enter value: "); scanf("%lld", &a); printf("calculating...\n"); i = a; while(i > 0){ add(a % i); i--; }; printUInt128(); getchar(); getchar(); return 0; }
Либо, чтобы было проще понять, вариант на дельфи, но (увы) без коментариев:
program Project1; {$APPTYPE CONSOLE} {$R *.res} uses Windows, SysUtils, Math; type Int128 = array [0..15] of Byte; var VeriBigInteger: Int128; procedure PrintInt128; var I: Integer; begin Write('0x'); for I := 15 downto 0 do Write(IntToHex(VeriBigInteger[I], 2)); Writeln; end; function FullAdder(A, B, P0: Boolean; var P: Boolean): Boolean; begin Result := False; P := False; if A and not B then Result := True; if B and not A then Result := True; if A and B then P := True; if not P0 then Exit; P := True; Result := not Result; if not A and not B then P := False; end; procedure Add(Value: Int64); var I: Integer; ABit, BBit, SFlag, PFlag: Boolean; HalfResult, BigValue: Int64; begin PFlag := False; HalfResult := 0; BigValue := PInt64(@VeriBigInteger[0])^; for I := 0 to 63 do begin ABit := (Int64(1) shl I and Value) > 0; BBit := (Int64(1) shl I and BigValue) > 0; SFlag := FullAdder(ABit, BBit, PFlag, PFlag); if SFlag then HalfResult := HalfResult or (Int64(1) shl I); end; PInt64(@VeriBigInteger[0])^ := HalfResult; HalfResult := 0; BigValue := PInt64(@VeriBigInteger[8])^; for I := 0 to 63 do begin BBit := (1 shl I and BigValue) > 0; SFlag := FullAdder(False, BBit, PFlag, PFlag); if SFlag then HalfResult := HalfResult or (Int64(1) shl I); end; PInt64(@VeriBigInteger[8])^ := HalfResult; end; function TstNative(X: int64): int64; var I: Int64; begin I := X; Result := 0; while I > 0 do begin Result := Result + (X mod I); Dec(I); end; end; procedure TstOld(X: int64); var I: Int64; begin ZeroMemory(@VeriBigInteger[0], SizeOf(Int128)); I := X; while I > 0 do begin Add(X mod I); Dec(I); end; PrintInt128; end; var N: int64; begin try repeat Readln(N); Writeln('0x', IntToHex(TstNative(N), 32)); TstOld(N); until N = 0; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; readln; end.
Сразу скажу - ЭТО работает, причем работает правильно (можно даже использовать для проверки), однако есть более грамотный и более быстрый вариант решения в 17 строчек кода, вместо процедур Add и FullAdder (на дельфи или на си - кому как удобнее) :)
Задача - попробовать найти данный вариант решения или предложить еще более оптимальный вариант :)
С наступающим :)
UPD: дам подсказку, график прохода по модулю выглядит вот так:
Определение верхнего лимита прогрессии:
X := Limit div (Z + 1) - (Z - (Limit mod (Z + 1)));
Где:
X - предел положительной вершины
Limit - значение числа N из оригинальной задачи
Z - текущее прирастание прогрессии (т.е. при Z = 1, прогрессия 0, 1, 2... при Z = 5, прогрессия 0, 5, 10... )
т.е. грубо идем от 0 до 1000 - пики выглядят:
499, 332, 247, 196, 165 и т.д....
Мы кембриджев не кончали, в верхней математике не сильно шарим - мы по простому
ОтветитьУдалитьtype
Int128 = packed record
case integer of
0: (i128: array [0..15] of byte);
1: (s0,s1: int64);
end;
var
R: int128;
i,N: int64;
Over: boolean;
begin
N:=Round(power(10,12));
R.s1:=0;
R.s0:=0;
i:=1;
Over:=false;
while i <= N do begin
R.s0:=R.s0+(N mod i);
Inc(i);
if (R.s0 < 0) and (not Over) then Over:=true;
else if (R.s0 >= 0) and Over then begin
Over:=false;
Inc(R.s1);
end;
end;
end;
Веселого нового года :)
А я все думаю - зачем люди строки нумеруют?
УдалитьВот и мне подарок к новому году , теперь знаю - чтобы глупая машина структуру кода не съедала.
1 type
2 Int128 = packed record
3 case integer of
4 0: (i128: array [0..15] of byte);
5 1: (s0,s1: int64);
6 end;
7
8 var
9 R: int128;
10 i,N: int64;
11 Over: boolean;
12
13 begin
14 i:=$7fffffffffffffff;
15 N:=Round(power(10,12));
16 R.s1:=0;
17 R.s0:=0;
18 i:=1;
19 Over:=false;
20 while i <= N do begin
21 R.s0:=R.s0+N mod i;
22 Inc(i);
23 if (R.s0 < 0) and (not Over) then begin
24 Over:=true;
25 end
26 else if (R.s0 >= 0) and Over then begin
27 Over:=false;
28 Inc(R.s1);
29 end;
30 end;
31 end;
А она все равно съела...
ОтветитьУдалитьВ комментариях подсветка кода и разметка не поддерживается :)
УдалитьПо самому коду, в принципе да. как-то так и надо, только ждать долго, около трех дней, поэтому нужно что-то более эффективное :)
Модуль и сложение никуда не денешь.
ОтветитьУдалитьБолее эффективное - только на ассемблере
Конечно денешь, в этом то собственно и есть суть задачи. Более того скажу, что самый быстрый из известных мне вариантов решения решает данную задачу для N = 10^12 за 73 миллисекунды :)
Удалить1 января решение будет опубликовано.
суть олимпиадных задач и состоит в знаниях и применениях алгоритмов для быстрого решения, а не решения в лоб. Так что естественно решаться задача будет не по данной в условии формуле прямым счетом.
УдалитьТак где же решение?
ОтветитьУдалитьОпубликовал
УдалитьYou are going to calculate N%1+ N%2+ N%3+...+ N%N - а это разве не арифметическая прогрессия?
ОтветитьУдалитьНе совсем, грубо говоря это набор арифметических прогрессий.
УдалитьСкажите, где можно найти вторую часть ответа на задачку номер 2?
ОтветитьУдалитьА я ее не публиковал, но там в кратце трюк заключается в восстановлении pwndOrg и снятия флага DCX_INUSE у структуры tagDCE при вызове GetDCEx с флагом DCX_INTERSECT
УдалитьА нужно сделать, чтобы было всем понятно, а то не совсем понятно. :)
УдалитьСтатью писать лень, вот тут я основные позиции вкратце накидал, сишный код взят из исходников w2k.
Удалитьhttp://rouse.drkb.ru/tmp/canvas.zip
Думаю не только мне интересно.
УдалитьДа и как-то странно выглядит, что нет 2-й части на ответ. Не хорошо это.
А заметочку можно за неделю осилить для блога, чтобы он жил.
Посмотрел код, честно говоря не понял откуда в такой простой задачке появились структуры и т.д. :)
Структуры - это скажем там внутренности GDI. А статьи пока что не будет, дело в том что когда я готовил вторую часть ответа и перепроверял себя наткнулся на один не понятный мне самому момент, без изучения которого ответ был бы не полным. А на его изучение требуется время, которого и так мало, поэтому решил все это отложить в сторонку. Как созрею - так сразу опубликую все целиком :)
Удалить