일단 누적합의 경우는 루프가 많다는 점이 가장 최적화 해야 할 부분이라고 생각했습니다. 그래서 먼저 수열을 정리하면서 풀어해쳤더니 복잡한 중간과정은 생략하고
항상 y = (end + start) * ((end - start + 1) / 2) 이라는 공식이 먼저 산출되었습니다.
하지만 이것은 실수계산인 경우이겠죠.
우리의 컴퓨터는 야속하게도 정수계산을 더욱 좋아하기 때문에 이를 정수계산만으로 해결하는 방법을 위해 다시 계산 공식을 수정해야 합니다.
그래서 한번 얼핏 상상해봤습니다. 그랬더니 start 부터 end 까지의 숫자갯수가 짝수인 경우는 그냥 위의 공식이 그대로 될법 했습니다.
하지만 홀수는 0.5씩 값이 정수계산시에 loss 납니다.
결국 어째저째 하여 정수계산시 발생하는 loss 는 start + ((end - start + 1) / 2) 라는 결론이 났습니다.
일단 공식을 좀더 정리해볼수 있을법 하지만 일단 이 이상은 머리아파서 그만 정리하겠습니다.
이제 코드로 만들어 내면 되는데 다음과 같이 우선 C로 해봤습니다.
int s_result, s_quota, s_range;
s_range = s_end - s_start + 1;
s_quota = s_range >> 1;
s_result = (s_end + s_start) * s_quota;
if(s_range & 1)s_result += s_quota + s_start;
잘됩니다. 드디어 루프없이 특정범위의 합을 계산할수 있는것이 가능하다는 증명이 되었습니다. ㅎㅎㅎ
이제 아래처럼 어셈블리로 코딩하였습니다. 역시 빠릅니다. 루프는 이제 누적합에서 사용하지 않아도 된다는 결론입니다.
.MODEL SMALL
.RADIX 0AH
; 테스트는 여러가지로 아래의 값을 변경하면서 해보세요.
DEF_BEGIN_VALUE = 1 ; 시작값
DEF_END_VALUE = 100 ; 끝값
ASSUME CS:SEG_CODE,DS:SEG_CODE,ES:NOTHING,SS:SEG_STACK
SEG_CODE SEGMENT PARA PUBLIC USE16 'CLASS_CODE'
L_ENTRY:
CLI
MOV AX, SEG_STACK
MOV SS, AX
MOV SP, OFFSET SEG_STACK:L_STACK_EOP
STI
MOV AX, SEG_CODE
MOV DS, AX
MOV ES, AX
; "DEF_BEGIN_VALUE"부터 "DEF_END_VALUE"까지 더하는 테스트
XOR CX, CX
MOV AX, DEF_END_VALUE
MOV BX, DEF_BEGIN_VALUE
ADD BX, AX
SUB AX, DEF_BEGIN_VALUE
INC AX
SHR AX, 1
JNC L_EVEN ; 짝수일때 L_EVEN으로 분기
ADD CX, AX
ADD CX, DEF_BEGIN_VALUE
L_EVEN:
MUL BX
ADD AX, CX
L_END_TEST:
; 결과값 AX의 확인을 위해 화면에 출력
MOV BX, 10
XOR CX, CX
L_PRINT_0:
XOR DX, DX
DIV BX
ADD DX, '0'
PUSH DX
INC CX
OR AX, AX
JNZ L_PRINT_0
L_PRINT_1:
POP DX
MOV AH, 02H
INT 21H
LOOP L_PRINT_1
; 프로그램 종료
MOV AX, 4C00H
INT 21H
JMP $
SEG_CODE ENDS
ASSUME CS:SEG_CODE,DS:SEG_CODE,ES:NOTHING,SS:SEG_STACK
SEG_STACK SEGMENT PARA STACK USE16 'CLASS_STACK'
DB 1024 DUP (?) ; 스택 공간 확보
L_STACK_EOP:
SEG_STACK ENDS
END L_ENTRY ; 시작점
; End of source
C 언어로 정리
#include <stdio.h>
int mysum(int s_start, int s_end)
{
int s_result, s_quota, s_range;
s_range = s_end - s_start + 1;
s_quota = s_range >> 1;
s_result = (s_end + s_start) * s_quota;
if(s_range & 1)s_result += s_quota + s_start;
return(s_result);
}
int main(void)
{
(void)fprintf(stdout, "1 부터 1000 까지 합 구하기 : %d\n", mysum(1, 1000));
return(0);
}
python 으로 정리
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
if __name__ == u"__main__":
start = 1
end = 100
irange = end - start + 1
quota = irange >> 1
isum = (end + start) * quota
if irange & 1: isum += quota + start
print("sum(%u ~ %u) is %u\n"%(start, end, isum))