Este post é uma reedição de um post meu no euromilhoes.com.

Em primeiro lugar, o que é o CSN? O CSN é o Número Sequencial Combinatório (Combinatorial Sequence Number) de uma dada combinação, quando estas se encontram ordenadas lexicograficamente, isto é, tendo como exemplo C(50,5), da [1, 2, 3, 4, 5] até à [46, 47, 48, 49, 50]. Neste caso, os CSN’s seriam o 1 e o 2.118.760.

Uma forma de calcular os CSN’s, e vice-versa, seria simplesmente construir todo o desdobramento até ao CSN pretendido, ou até à chave pretendida. Don’t. É estúpido, é um desperdício de ciclos de processamento e é péssima programação. Sobretudo, se já alguém pensou no assunto e se os algoritmos são do domínio público. Mesmo que não saibam, pesquisem antes de cometer uma barbaridade deste tamanho.

Vamos então aos ditos…

O algoritmo para calcular a combinação a partir do CSN é uma engenhosa obra prima de B. P. Buckles e M. Lyabanon, publicada em 1974.

função csnParaCombinação(csn, n, k)
    seja limite_inferior = 0;
    seja r = 0;
    seja combinação = [0...k-1]
 
    para cada i, sendo i = 0, enquanto i < k - 1, incrementar i
        combinação[i] = 0;
        se(i != 0) combinação[i] = combinação[i - 1];

        fazer
            combinação[i] =  combinação[i] + 1;
            r = númeroDeCombinações(n - combinação[i], (k - 1) - i); // lembram-se desta função?
            limite_inferior = limite_inferior + r;
        enquanto (limite_inferior < csn);
 
        limite_inferior = limite_inferior - r;
    fim de para;
 
    combinação[k - 1] = combinação[k - 2] + csn - limite_inferior;
 
    devolve combinação;
fim de função;

Para começar, quero-vos dizer que parece bem mais complicado do que na realidade é. Os comentários na minha implementação em C# poderão ajudar-vos a perceber melhor.

Depois, reparem como a última posição da combinação é tratada à parte, duma maneira perfeitamente elegante, usando os valores do CSN pedido e do último limite inferior calculado.

Mas passemos, então, à manobra inversa, calcular o CSN a partir da combinação. O seguinte algoritmo não tem, que eu saiba, autores oficiais. A sua simplicidade decorre de ser um negativo quase perfeito da função anterior.

função combinaçãoParaCsn(combinação, n)
    seja k = combinação.tamanho();
    seja limite_inferior = 0;
    seja r= 0;
 
    para cada i, sendo i = 1, enquanto i <= k, incrementar i
        r = n - combinação[k - i];
        se (r >= i)
            limite_inferior = limite_inferior + númeroDeCombinações(r, i);
        fim de se;
    fim de para;
 
    devolve númeroDeCombinações(n, k) - limite_inferior;
fim de função;

Podem ver, na segunda instrução do para, que a combinação está a ser percorrida de trás para a frente, e o limite inferior está a ser contado como o que falta até ao fim, sendo finalmente subtraído ao número de combinações total para nos dar o CSN.

O código fonte das minhas implementações em C# fazem parte da classe CombTools, disponível no projecto Combinatorica.

Partilhar no Sapo Links Partilhar no del.icio.us Partilhar no Digg Partilhar no Twitter Partilhar no StumbleUpon Partilhar no MySpace Partilhar no Facebook

Comentários Comentários Imagem do Feed RSS

1 2

Marco

Olá, Helder.

Leia esta posta sobre como calcular o número de combinações. Vai reparar que a sua forma de calcular é pouco prática, e rebenta bastante cedo, visto que a função factorial(n) vai rebentar com n > 12.

Helder

Oi Aurélio,

Eu também estou convertendo a biblioteca para Delphi... vi seu comentário, e acabei implementando a função numeroDeCombinacoes desta maneira:

function numeroDeCombinacoes (n, k : integer) : double;
begin
    if n < k then
    begin
        result := 0.0;
    end
    else
    begin
        result := fatorial(n) / (fatorial(k) * fatorial (n-k));
    end;
end;

function fatorial (n: integer):integer;
var i: integer;
begin
    Result := 1;
    for i := 2 to n do
        Result := Result * i;
end;

 

aurelioleonel

Boa noite campeão

  Graças a Deus e a vc eu terminei os algoritimos do CSN em delphi que vc postou aqui como exemplo, o minimo que eu posso lhe contribuir e enviando oas fontes em delphi para vc, é só vc mandar seu e-mail para aurelioleonel@gmail.com que com prazer eu te enviarei, para vc testar, tem limitações pois algumas variavéis eu defeni com inteiro, e se vc colocar  100, 20 vai dar erro, mais isso é corrigivel pois para mim só interassa de 25,15 pois jogo na lotofácil ok!!! fico no aguardo e tenha uma boa sorte

Marcos Aurélio

Marco

!= é diferente. Em Delphi acho que é <>.

aurelioleonel

Ola Campeão

   Mais uma vez obrigado, a respeito do Begin..End da linha 11 do algorito que eu fiz, tambem achei que faltava, mais eu fiz masi de 15 testes, comparando com outro programa que eu tenho e os valores foram iguais.

  Quando eu chegar em casa vou analizar o que vc me postou e retornarei assim que fizer os testes OK!

A proposito o que siguinifica o ponto de exclamação neste item (i != 0)

Desde já agradeço

Marcos Aurélio

Marco

Estive a rever e a formatar o código que colocou e acho que falta ali um begin e um end no if da linha 11...

Tem estado a bater certo? Faça vários testes com resultados conhecidos; se essa função ficar engatada, daqui para a frente só vai piorar...

Marco

Começando pelo fim, ponto 3: e se fizer var combinacao: Array of Integer; e logo a seguir SetLength(combinacao, k - 1);? É que se puser lá um número inteiro directamente, perde a flexibilidade da função.

Ponto 1. Sim, cada item do array é inicializado a zero. Há linguagens que não precisam disto, mas o Delphi acho que sim.

Ponto 2.1. O então é logo a seguir. Como as linguagens em que costumo trabalhar não têm then, costumo esquecer-me. Deverá ler-se se(i != 0) então combinação[i] = combinação[i - 1];

Ponto 2.2. O fazer ... enquanto é uma estrutura repeat ... until

aurelioleonel

Campeão Bom Dia

  Otem consegui fazer a funcão

numeroDeCombinacoes(n, k : Integer) ficou perfeita

veja o algoritimo

Function numeroDeCombinacoes(n, k : Integer) : Variant;
Var Dif,  i : Integer;
Combinacoes : Double;
Begin
	If n < k Then Abort; // o valor de n não pode ser menor que k, com este comando nada e  executado  

	// Iniciando
	I := 0;
	Dif := n - k ;

	If k < Dif  Then
		Dif := k;
		k := n - Dif;

	Combinacoes := k +  1;

	If Dif = 0 Then
		Combinacoes := 1
	Else If  Dif >= 2 Then
		For i := 2 To dif Do //enquanto i não chegar ao valor de dif
			combinacoes := (combinacoes * (k + i)) / i;

	Result := Combinacoes;  // devolve a quantidade de combinações de n,k
	//exemplo  numeroDeCombinacoes(10, 5) é igual a 252 combinações
End;

Só estou apanhando na funcao

CsnParaCombinacao(csn, n, k)

Vamos la as duvidas

1-

para cada i, sendo i = 0, enquanto i < k - 1, incrementar i      combinação[i] = 0;

não estou entendendo este laço

todo array tem que receber zero primeiro?

2-

se(i != 0) combinação[i] = combinação[i - 1];       fazer         combinação[i] =  combinação[i] + 1;

não estou vendo o então: e esse "fazer" é o que?

3-

combinacao[0..k-1] o delphi não aceita, pode ser um numero fixo assim combinacao[0..24]

Desde ja agradeço

Marcos aurélio

 

aurelioleonel

Campeão boa Noite

    Mais uma vez muito obrigado agora ja dar para entender o algoritimo, mais ainda vai sugir duvidas e eu vou aos seus pés lhe perguntas, assim que estiver pronto, eu postare pedidoo seu e-mailpra viaros fontes do projeto, e ele completo ok uma abraço

Marcos Aurélio

Marco

Boas noites, Aurelio.

Essa função, numeroDeCombinações, está aqui explicada (é a última). Sim, é preciso implementar também.

A função tamanho() devolve o tamanho do array. Em Delphi, há-de ser qualquer coisa do estilo k = Length(oMeuArray).

Sim, as CSN são isso mesmo; por exemplo, para combinações de 8, 5 a 5, o CSN 1 é 1-2-3-4-5, o CSN 2 é 1-2-3-4-6, o CSN 3 é 1-2-3-4-7, e por aí fora até ao CSN 56, que é 4-5-6-7-8.

1 2

Comentários Deixar um comentário

Tem que efectuar login para poder deixar comentários!

Se não está registado, registe-se aqui.
 Doar (via PayPal)
 Categorias
 Arquivo
 Projectos em Destaque
 Últimas Postas no Blog
 Últimos Comentários do Blog
Yet Another Blog» Arquivo » Liberdade de Expressão na Blogosfera @ Seg. 10 Out. 11 07:10