Soma segura de estouro

13

Suponha que eu receba números inteiros de largura fixa (ou seja, eles se encaixam em um registro de largura w ), a 1 , a 2 , a n de modo que sua soma a 1 + a 2 + + a n = S também se encaixe em um registro de largura w .nwa1,a2,ana1+a2++an=Sw

Parece-me que sempre podemos permutar os números para modo que cada soma de prefixo S i = b 1 + b 2 + + b i também se ajuste a um registro de largura w .b1,b2,bnSi=b1+b2++biw

Basicamente, a motivação é calcular a soma em máquinas de registro de largura fixa sem ter que se preocupar com estouros de número inteiro em qualquer estágio intermediário.S=Sn

Existe um rápido (tempo de preferência linear) algoritmo para encontrar uma tal permutao (assumindo a são dadas como uma matriz de entrada)? (ou diga se essa permutação não existe).ai

Aryabhata
fonte
3
Acompanhamento: Detecção de estouro na soma - existe um método mais rápido que leva em consideração os recursos típicos do processador?
Gilles 'SO- stop be evil'
1
Basta usar dois registros de complemento e somar. Mesmo se estourar no meio, sua pré-condição garante que os estouros sejam cancelados e o resultado estará correto. : P
CodesInChaos
@CodeInChaos: Isso é realmente verdade?
Aryabhata
1
Acho que sim. Você está trabalhando essencialmente em um módulo de grupo 2 ^ n, onde escolhe a representação canônica de -2^(n-1)para 2^(n-1)-1. É claro que requer o complemento de dois e um comportamento de estouro bem definido, mas em uma linguagem como C # deve funcionar.
código é o seguinte
@CodeInChaos: Não existem duas possibilidades que dão o mesmo módulo restante ? Você está basicamente dizendo, independentemente da ordem, um deles nunca pode acontecer. Ou eu estou esquecendo de alguma coisa? 2n
Aryabhata

Respostas:

10

Estratégia
O algoritmo de tempo linear a seguir adota a estratégia de pairar em torno de , escolhendo números positivos ou negativos com base no sinal da soma parcial. Ele processa previamente a lista de números; calcula a permutação da entrada on-the-fly , enquanto executa a adição.0

Algoritmo

  1. Particionar em um duas listas, os elementos positivos P e os elementos negativos M . Os zeros podem ser filtrados.a1,,anPM
  2. Deixe .Sum=0
  3. Embora as duas listas não estejam vazias
  4.       Se { S u m : = S u m + cabeça ( H ) ;Sum>0Sum:=Sum+head(M) ; }M:=tail(M)
  5.       outro { ; P : = cauda ( P ) ; }Sum:=Sum+head(P)P:=tail(P)
  6. Quando uma das duas listas fica vazio, adicione o resto da lista restante para .S

Correção A
correção pode ser estabelecida usando um argumento indutivo direto no comprimento da lista de números.

Em primeiro lugar, provar que se são todos positivos (ou todos negativos), e sua soma não causa excesso, em seguida, nem as somas de prefixo. Isso é direto.a1,,an

Em segundo lugar, provar que é dentro de limites é uma invariante do ciclo do algoritmo. Claramente, isto é verdade em cima da entrada para o circuito, como S u m = 0 . Agora, se S u m > 0 , adicionando um número negativo que está dentro de limites para a S u m não causa S u m para ir fora dos limites. Da mesma forma, quando S u m 0, adicionar um número positivo dentro dos limites da soma não causa S u m sair dos limites. Assim, ao sair do loop,SumSum=0Sum>0SumSumSum0Svocêm é dentro de limites.Svocêm

Agora, o primeiro resultado pode ser aplicado e, juntos, são suficientes para provar que a soma nunca sai dos limites.

Dave Clarke
fonte
Para uma implementação eficiente no local, execute a) Particionamento por Quicksort (a variante de dois ponteiros) com pivô implícito e, em seguida, b) some, movendo um ponteiro pela área com resp negativo. números positivos. 0 0
Raphael