Como escolho um elemento aleatório de um conjunto? Estou particularmente interessado em escolher um elemento aleatório de um HashSet ou LinkedHashSet, em Java. Soluções para outros idiomas também são bem-vindas.
Você deve especificar algumas condições para ver se é realmente isso que você deseja. - Em que horas você estará selecionando um elemento aleatório? - Os dados precisam ser armazenados em um HashSet ou LinkedHashSet, e não são acessíveis aleatoriamente. - O hash está grande? As chaves são pequenas?
David Nehme 25/09/08
Respostas:
88
int size = myHashSet.size();int item =newRandom().nextInt(size);// In real life, the Random object should be rather more shared than thisint i =0;for(Object obj : myhashSet){if(i == item)return obj;
i++;}
Se myHashSet for grande, será uma solução bastante lenta, pois, em média, (n / 2) serão necessárias iterações para encontrar o objeto aleatório.
Daniel
6
se seus dados estiverem em um conjunto de hash, você precisará de O (n) tempo. Não há como contornar se você estiver apenas escolhendo um único elemento e os dados forem armazenados em um HashSet.
David Nehme 25/09/08
8
@ David Nehme: Esta é uma desvantagem na especificação do HashSet em Java. No C ++, é típico poder acessar diretamente os buckets que compõem o hashset, o que nos permite selecionar com mais eficiência elementos aleatórios. Se elementos aleatórios forem necessários em Java, pode valer a pena definir um conjunto de hash customizado que permita ao usuário olhar sob o capô. Veja [docs do boost] [1] para um pouco mais sobre isso. [1] boost.org/doc/libs/1_43_0/doc/html/unordered/buckets.html
Aaron McDaid
11
Se o conjunto não for alterado por vários acessos, você poderá copiá-lo em uma matriz e acessar O (1). Basta usar myHashSet.toArray ()
ykaganovich
2
@ykaganovich isso não tornaria as coisas piores, já que o conjunto teria que ser copiado para uma nova matriz? docs.oracle.com/javase/7/docs/api/java/util/... "este método deve alocar um novo array mesmo se esta coleção é apoiado por um conjunto"
Mas isso só funciona com Listas, ou seja, estruturas que possuem uma função .get ().
bourbaki4481472
4
@ bourbaki4481472 está absolutamente correto. Isso funciona apenas para as coleções que estendem a Listinterface, não a Setinterface discutida pelo OP.
Thomas
31
Solução rápida para Java usando an ArrayListe a HashMap: [elemento -> índice].
Motivação: eu precisava de um conjunto de itens com RandomAccesspropriedades, especialmente para escolher um item aleatório do conjunto (consulte o pollRandommétodo). A navegação aleatória em uma árvore binária não é precisa: as árvores não são perfeitamente equilibradas, o que não levaria a uma distribuição uniforme.
publicclassRandomSet<E>extendsAbstractSet<E>{List<E> dta =newArrayList<E>();Map<E,Integer> idx =newHashMap<E,Integer>();publicRandomSet(){}publicRandomSet(Collection<E> items){for(E item : items){
idx.put(item, dta.size());
dta.add(item);}}@Overridepublicboolean add(E item){if(idx.containsKey(item)){returnfalse;}
idx.put(item, dta.size());
dta.add(item);returntrue;}/**
* Override element at position <code>id</code> with last element.
* @param id
*/public E removeAt(int id){if(id >= dta.size()){returnnull;}
E res = dta.get(id);
idx.remove(res);
E last = dta.remove(dta.size()-1);// skip filling the hole if last is removedif(id < dta.size()){
idx.put(last, id);
dta.set(id, last);}return res;}@Overridepublicboolean remove(Object item){@SuppressWarnings(value ="element-type-mismatch")Integer id = idx.get(item);if(id ==null){returnfalse;}
removeAt(id);returntrue;}public E get(int i){return dta.get(i);}public E pollRandom(Random rnd){if(dta.isEmpty()){returnnull;}int id = rnd.nextInt(dta.size());return removeAt(id);}@Overridepublicint size(){return dta.size();}@OverridepublicIterator<E> iterator(){return dta.iterator();}}
Bem, isso funcionaria, mas a questão era sobre a interface Set. Essa solução força os usuários a ter referências de tipo concreto do RandomSet.
Johan Tidén 5/05
Eu realmente gosto desta solução, mas não é thread-safe, imprecisões entre o mapa ea lista pode ocorrer, então eu gostaria de acrescentar alguns blocos sincronizados
Kostas Chalkias
@KonstantinosChalkias as coleções internas também não são seguras para threads. Somente aqueles com o nome Concurrentsão realmente seguros, os que estão envoltos Collections.synchronized()são semi-seguros. Além disso, o OP não disse nada sobre concorrência, portanto, essa é uma resposta válida e boa.
TWIStErRob 03/08/19
O iterador retornado aqui não deve ser capaz de remover elementos dta(isso pode ser alcançado por meio de goiabas, Iterators.unmodifiableIteratorpor exemplo). Caso contrário, as implementações padrão de, por exemplo, removeAll e retemAllAll no AbstractSet e seus pais que trabalham com esse iterador atrapalharão o seu RandomSet!
muued 12/08/16
Ótima solução. Na verdade, você pode usar uma árvore se cada nó contiver o número de nós na subárvore que ele enraíza. Em seguida, calcule um real aleatório em 0..1 e tome uma decisão de três vias ponderada (selecione o nó atual ou desça na subárvore esquerda ou direita) em cada nó com base nas contagens do nó. Mas a sua solução é muito melhor.
Gene
29
Isso é mais rápido que o loop for-each na resposta aceita:
int index = rand.nextInt(set.size());Iterator<Object> iter = set.iterator();for(int i =0; i < index; i++){
iter.next();}return iter.next();
A construção for-each chama Iterator.hasNext()cada loop, mas desde então index < set.size(), essa verificação é desnecessária. Eu vi um aumento de 10-20% na velocidade, mas YMMV. (Além disso, isso é compilado sem a necessidade de adicionar uma declaração de retorno extra.)
Observe que esse código (e a maioria das outras respostas) pode ser aplicado a qualquer coleção, não apenas a conjunto. Na forma genérica do método:
publicstatic<E> E choice(Collection<?extends E> coll,Random rand){if(coll.size()==0){returnnull;// or throw IAE, if you prefer}int index = rand.nextInt(coll.size());if(coll instanceofList){// optimizationreturn((List<?extends E>) coll).get(index);}else{Iterator<?extends E> iter = coll.iterator();for(int i =0; i < index; i++){
iter.next();}return iter.next();}}
Se você quiser fazer isso em Java, considere copiar os elementos em algum tipo de coleção de acesso aleatório (como um ArrayList). Porque, a menos que seu conjunto seja pequeno, o acesso ao elemento selecionado será caro (O (n) em vez de O (1)). [ed: list copy também é O (n)]
Como alternativa, você pode procurar outra implementação de Conjunto que melhor atenda aos seus requisitos. O ListOrderedSet da Commons Collections parece promissor.
Copiar para uma lista custará O (n) no tempo e também usará memória (n). Então, por que essa seria uma escolha melhor do que buscar diretamente no mapa?
Mdma
12
Depende de quantas vezes você deseja escolher no aparelho. A cópia é uma operação única e, em seguida, você pode escolher entre o conjunto quantas vezes for necessário. Se você está escolhendo apenas um elemento, sim, a cópia não torna as coisas mais rápidas.
Dan Dyer
É apenas uma operação única, se você quiser escolher com repetição. Se você deseja que o item escolhido seja removido do conjunto, retorne para O (n).
precisa saber é o seguinte
12
No Java 8:
static<E> E getRandomSetElement(Set<E> set){return set.stream().skip(newRandom().nextInt(set.size())).findFirst().orElse(null);}
Set<Integer> set =newLinkedHashSet<Integer>(3);
set.add(1);
set.add(2);
set.add(3);Random rand =newRandom(System.currentTimeMillis());int[] setArray =(int[]) set.toArray();for(int i =0; i <10;++i){System.out.println(setArray[rand.nextInt(set.size())]);}
Isso é incrivelmente ineficiente. Seu construtor ArrayList chama .toArray () no conjunto fornecido. ToArray (na maioria das implementações de coleção padrão, se não todas), interage com toda a coleção, preenchendo uma matriz à medida que ela é executada. Em seguida, você embaralha a lista, que troca cada elemento com um elemento aleatório. Você seria muito melhor simplesmente iterando o conjunto para um elemento aleatório.
precisa
4
Isso é idêntico à resposta aceita (Khoth), mas com o desnecessário sizee as ivariáveis removidas.
int random =newRandom().nextInt(myhashSet.size());for(Object obj : myhashSet){if(random--==0){return obj;}}
Apesar de acabar com as duas variáveis acima mencionadas, a solução acima ainda permanece aleatória, porque confiamos no aleatório (começando em um índice selecionado aleatoriamente) para diminuir 0cada vez mais a iteração.
C ++. Isso deve ser razoavelmente rápido, pois não requer iteração em todo o conjunto ou classificação. Isso deve funcionar imediatamente com os compiladores mais modernos, assumindo que eles suportem tr1 . Caso contrário, pode ser necessário usar o Boost.
Os documentos do Boost são úteis aqui para explicar isso, mesmo que você não use o Boost.
O truque é fazer uso do fato de que os dados foram divididos em intervalos e identificar rapidamente um intervalo escolhido aleatoriamente (com a probabilidade apropriada).
//#include <boost/unordered_set.hpp> //using namespace boost;#include <tr1/unordered_set>
using namespace std::tr1;#include <iostream>#include <stdlib.h>#include <assert.h>
using namespace std;int main(){
unordered_set<int> u;
u.max_load_factor(40);for(int i=0; i<40; i++){
u.insert(i);
cout <<' '<< i;}
cout << endl;
cout <<"Number of buckets: "<< u.bucket_count()<< endl;for(size_t b=0; b<u.bucket_count(); b++)
cout <<"Bucket "<< b <<" has "<< u.bucket_size(b)<<" elements. "<< endl;for(size_t i=0; i<20; i++){size_t x = rand()% u.size();
cout <<"we'll quickly get the "<< x <<"th item in the unordered set. ";size_t b;for(b=0; b<u.bucket_count(); b++){if(x < u.bucket_size(b)){break;}else
x -= u.bucket_size(b);}
cout <<"it'll be in the "<< b <<"th bucket at offset "<< x <<". ";
unordered_set<int>::const_local_iterator l = u.begin(b);while(x>0){
l++;assert(l!=u.end(b));
x--;}
cout <<"random item is "<<*l <<". ";
cout << endl;}}
A solução acima fala em termos de latência, mas não garante a mesma probabilidade de cada índice selecionado.
Se isso precisar ser considerado, tente a amostragem do reservatório. http://en.wikipedia.org/wiki/Reservoir_sampling . Collections.shuffle () (como sugerido por poucos) usa um desses algoritmos.
Somente [1,2,3,4,5,6] não é um conjunto, mas uma lista, pois não suporta coisas como pesquisas rápidas.
22416 Thomas Ahle
Você ainda pode fazer: >>> random.choice (list (set (range (5)))) >>> 4 Não é o ideal, mas funcionará se você precisar.
SapphireSun
1
Você não pode simplesmente obter o tamanho / comprimento do conjunto / matriz, gerar um número aleatório entre 0 e o tamanho / comprimento e chamar o elemento cujo índice corresponde a esse número? HashSet tem um método .size (), tenho certeza.
Em psuedocode -
function randFromSet(target){
var targetLength:uint = target.length()
var randomIndex:uint = random(0,targetLength);return target[randomIndex];}
Isso funciona apenas se o contêiner em questão oferecer suporte à pesquisa aleatória de índice. Muitas implementações de contêineres não o fazem (por exemplo, tabelas de hash, árvores binárias, listas vinculadas).
Random random =newRandom((int)DateTime.Now.Ticks);OrderedDictionary od =newOrderedDictionary();
od.Add("abc",1);
od.Add("def",2);
od.Add("ghi",3);
od.Add("jkl",4);int randomIndex = random.Next(od.Count);Console.WriteLine(od[randomIndex]);// Can access via index or key value:Console.WriteLine(od[1]);Console.WriteLine(od["def"]);
parece que eles foram prejudicados porque o dicionário java de baixa qualidade (ou o chamado LinkedHashSet, qualquer que seja o inferno) não pode ser "acessado aleatoriamente" (que está sendo acessado por chave, eu acho). A porcaria java me faz rir muito
Federico Berasategui
1
Solução Javascript;)
function choose (set){return set[Math.floor(Math.random()* set.length)];}
var set =[1,2,3,4], rand = choose (set);
Ou alternativamente:
Array.prototype.choose = function (){returnthis[Math.floor(Math.random()*this.length)];};[1,2,3,4].choose();
Isso funciona apenas para listas, certo? Com ELTele poderia funcionar para qualquer sequência.
Ken
1
No Mathematica:
a ={1,2,3,4,5}
a[[⌈Length[a]Random[]⌉]]
Ou, nas versões recentes, simplesmente:
RandomChoice[a]
Isso recebeu um voto negativo, talvez por falta de explicação, então aqui está:
Random[]gera uma flutuação pseudo-aleatória entre 0 e 1. Isso é multiplicado pelo comprimento da lista e, em seguida, a função de teto é usada para arredondar para o próximo número inteiro. Este índice é então extraído a.
Como a funcionalidade da tabela de hash é frequentemente feita com regras no Mathematica e as regras são armazenadas em listas, pode-se usar:
a ={"Badger"->5,"Bird"->1,"Fox"->3,"Frog"->2,"Wolf"->4};
Por diversão, escrevi um RandomHashSet baseado em amostras de rejeição. É um pouco hacky, já que o HashMap não nos permite acessar sua tabela diretamente, mas deve funcionar muito bem.
Ele não usa memória extra e o tempo de pesquisa é O (1) amortizado. (Como o java HashTable é denso).
Exatamente o que eu estava pensando! Melhor resposta!
mmm
Na verdade, voltando a ele, acho que isso não é bastante uniforme, se o hashmap tiver muitas colisões e fizermos muitas consultas. Isso ocorre porque o hashmap java usa buckets / encadeamento e esse código sempre retornará o primeiro elemento no bucket específico. Ainda somos uniformes quanto à aleatoriedade da função hash.
Com a Goiaba , podemos fazer um pouco melhor do que a resposta de Khoth:
publicstatic E random(Set<E> set){int index = random.nextInt(set.size();if(set instanceofImmutableSet){// ImmutableSet.asList() is O(1), as is .get() on the returned listreturn set.asList().get(index);}returnIterables.get(set, index);}
Se você realmente deseja selecionar "qualquer" objeto do Set, sem nenhuma garantia de aleatoriedade, o mais fácil é obter o primeiro retornado pelo iterador.
Set<Integer> s =...Iterator<Integer> it = s.iterator();if(it.hasNext()){Integer i = it.next();// i is a "random" object from set}
Porém, isso não será uma escolha aleatória. Imagine realizar a mesma operação no mesmo conjunto várias vezes. Eu acho que a ordem será a mesma.
Menezes Sousa
0
Uma solução genérica usando a resposta de Khoth como ponto de partida.
/**
* @param set a Set in which to look for a random element
* @param <T> generic type of the Set elements
* @return a random element in the Set or null if the set is empty
*/public<T> T randomElement(Set<T> set){int size = set.size();int item = random.nextInt(size);int i =0;for(T obj : set){if(i == item){return obj;}
i++;}returnnull;}
Infelizmente, isso não pode ser feito com eficiência (melhor que O (n)) em qualquer um dos contêineres da Biblioteca Padrão.
Isso é estranho, pois é muito fácil adicionar uma função de seleção aleatória a conjuntos de hash e também a conjuntos binários. Em um conjunto de hash não esparso, você pode tentar entradas aleatórias até receber um hit. Para uma árvore binária, você pode escolher aleatoriamente entre a subárvore esquerda ou direita, com no máximo O (log2) etapas. Implementei uma demonstração dos itens abaixo abaixo:
import random
classNode:
def __init__(self, object):
self.object = object
self.value = hash(object)
self.size =1
self.a = self.b =NoneclassRandomSet:
def __init__(self):
self.top =None
def add(self, object):"""Add any hashable object to the set.Notice:Inthis simple implementation you shouldn't add two
identical items."""new=Node(object)if not self.top: self.top =newelse: self._recursiveAdd(self.top,new)
def _recursiveAdd(self, top,new):
top.size +=1ifnew.value < top.value:if not top.a: top.a =newelse: self._recursiveAdd(top.a,new)else:if not top.b: top.b =newelse: self._recursiveAdd(top.b,new)
def pickRandom(self):"""Pick a random item in O(log2) time.Does a maximum of O(log2) calls to random as well."""return self._recursivePickRandom(self.top)
def _recursivePickRandom(self, top):
r = random.randrange(top.size)if r ==0:return top.object
elif top.a and r <= top.a.size:return self._recursivePickRandom(top.a)return self._recursivePickRandom(top.b)if __name__ =='__main__':
s =RandomSet()for i in [5,3,7,1,4,6,9,2,8,0]:
s.add(i)
dists =[0]*10for i in xrange(10000):
dists[s.pickRandom()]+=1
print dists
Eu tenho [995, 975, 971, 995, 1057, 1004, 966, 1052, 984, 1001] como saída, então a distribuição parece boa.
Eu lutei com o mesmo problema para mim e ainda não decidi enfrentar o ganho de desempenho dessa escolha mais eficiente que vale a pena a sobrecarga do uso de uma coleção baseada em python. É claro que eu poderia refiná-lo e traduzi-lo para C, mas isso é muito trabalho para mim hoje :)
Uma razão pela qual acho que isso não foi implementado em uma árvore binária é que esse método não selecionaria itens de maneira uniforme. Como são nós sem filhos esquerdo / direito, pode ocorrer uma situação em que o filho esquerdo contenha mais itens que o filho direito (ou vice-versa), isso tornaria mais provável a escolha de um item no filho direito (ou esquerdo).
Willem Van Onsem
1
@CommuSoft: É por isso que armazeno o tamanho de cada subárvore, para poder escolher minhas probabilidades com base nessas.
Respostas:
fonte
Um pouco relacionado Você sabia:
Existem métodos úteis
java.util.Collections
para embaralhar coleções inteiras:Collections.shuffle(List<?>)
eCollections.shuffle(List<?> list, Random rnd)
.fonte
List
interface, não aSet
interface discutida pelo OP.Solução rápida para Java usando an
ArrayList
e aHashMap
: [elemento -> índice].Motivação: eu precisava de um conjunto de itens com
RandomAccess
propriedades, especialmente para escolher um item aleatório do conjunto (consulte opollRandom
método). A navegação aleatória em uma árvore binária não é precisa: as árvores não são perfeitamente equilibradas, o que não levaria a uma distribuição uniforme.fonte
Concurrent
são realmente seguros, os que estão envoltosCollections.synchronized()
são semi-seguros. Além disso, o OP não disse nada sobre concorrência, portanto, essa é uma resposta válida e boa.dta
(isso pode ser alcançado por meio de goiabas,Iterators.unmodifiableIterator
por exemplo). Caso contrário, as implementações padrão de, por exemplo, removeAll e retemAllAll no AbstractSet e seus pais que trabalham com esse iterador atrapalharão o seuRandomSet
!Isso é mais rápido que o loop for-each na resposta aceita:
A construção for-each chama
Iterator.hasNext()
cada loop, mas desde entãoindex < set.size()
, essa verificação é desnecessária. Eu vi um aumento de 10-20% na velocidade, mas YMMV. (Além disso, isso é compilado sem a necessidade de adicionar uma declaração de retorno extra.)Observe que esse código (e a maioria das outras respostas) pode ser aplicado a qualquer coleção, não apenas a conjunto. Na forma genérica do método:
fonte
Se você quiser fazer isso em Java, considere copiar os elementos em algum tipo de coleção de acesso aleatório (como um ArrayList). Porque, a menos que seu conjunto seja pequeno, o acesso ao elemento selecionado será caro (O (n) em vez de O (1)). [ed: list copy também é O (n)]
Como alternativa, você pode procurar outra implementação de Conjunto que melhor atenda aos seus requisitos. O ListOrderedSet da Commons Collections parece promissor.
fonte
No Java 8:
fonte
Em Java:
fonte
fonte
Isso é idêntico à resposta aceita (Khoth), mas com o desnecessário
size
e asi
variáveis removidas.Apesar de acabar com as duas variáveis acima mencionadas, a solução acima ainda permanece aleatória, porque confiamos no aleatório (começando em um índice selecionado aleatoriamente) para diminuir
0
cada vez mais a iteração.fonte
if (--random < 0) {
, onderandom
chega-1
.Solução Clojure:
fonte
nth
elemento, você também deve atravessá-loseq
.Perl 5
Aqui está uma maneira de fazer isso.
fonte
C ++. Isso deve ser razoavelmente rápido, pois não requer iteração em todo o conjunto ou classificação. Isso deve funcionar imediatamente com os compiladores mais modernos, assumindo que eles suportem tr1 . Caso contrário, pode ser necessário usar o Boost.
Os documentos do Boost são úteis aqui para explicar isso, mesmo que você não use o Boost.
O truque é fazer uso do fato de que os dados foram divididos em intervalos e identificar rapidamente um intervalo escolhido aleatoriamente (com a probabilidade apropriada).
fonte
A solução acima fala em termos de latência, mas não garante a mesma probabilidade de cada índice selecionado.
Se isso precisar ser considerado, tente a amostragem do reservatório. http://en.wikipedia.org/wiki/Reservoir_sampling .
Collections.shuffle () (como sugerido por poucos) usa um desses algoritmos.
fonte
Como você disse que "soluções para outros idiomas também são bem-vindas", aqui está a versão do Python:
fonte
Você não pode simplesmente obter o tamanho / comprimento do conjunto / matriz, gerar um número aleatório entre 0 e o tamanho / comprimento e chamar o elemento cujo índice corresponde a esse número? HashSet tem um método .size (), tenho certeza.
Em psuedocode -
fonte
PHP, assumindo "set" é uma matriz:
As funções do Mersenne Twister são melhores, mas não há equivalente em MT ao array_rand no PHP.
fonte
O ícone possui um tipo de conjunto e um operador de elemento aleatório, unário "?", Portanto, a expressão
produzirá um número aleatório entre 1 e 5.
A semente aleatória é inicializada como 0 quando um programa é executado, para produzir resultados diferentes em cada execução.
randomize()
fonte
Em c #
fonte
Solução Javascript;)
Ou alternativamente:
fonte
Em lisp
fonte
ELT
ele poderia funcionar para qualquer sequência.No Mathematica:
Ou, nas versões recentes, simplesmente:
Isso recebeu um voto negativo, talvez por falta de explicação, então aqui está:
Random[]
gera uma flutuação pseudo-aleatória entre 0 e 1. Isso é multiplicado pelo comprimento da lista e, em seguida, a função de teto é usada para arredondar para o próximo número inteiro. Este índice é então extraídoa
.Como a funcionalidade da tabela de hash é frequentemente feita com regras no Mathematica e as regras são armazenadas em listas, pode-se usar:
fonte
Que tal apenas
fonte
Por diversão, escrevi um RandomHashSet baseado em amostras de rejeição. É um pouco hacky, já que o HashMap não nos permite acessar sua tabela diretamente, mas deve funcionar muito bem.
Ele não usa memória extra e o tempo de pesquisa é O (1) amortizado. (Como o java HashTable é denso).
fonte
O mais fácil com o Java 8 é:
onde
n
é um número inteiro aleatório. É claro que tem menos desempenho do que com ofor(elem: Col)
fonte
Com a Goiaba , podemos fazer um pouco melhor do que a resposta de Khoth:
fonte
PHP, usando MT:
fonte
você também pode transferir o conjunto para o array usar o array, provavelmente ele funcionará em pequena escala.
fonte
Se você realmente deseja selecionar "qualquer" objeto do
Set
, sem nenhuma garantia de aleatoriedade, o mais fácil é obter o primeiro retornado pelo iterador.fonte
Uma solução genérica usando a resposta de Khoth como ponto de partida.
fonte
Infelizmente, isso não pode ser feito com eficiência (melhor que O (n)) em qualquer um dos contêineres da Biblioteca Padrão.
Isso é estranho, pois é muito fácil adicionar uma função de seleção aleatória a conjuntos de hash e também a conjuntos binários. Em um conjunto de hash não esparso, você pode tentar entradas aleatórias até receber um hit. Para uma árvore binária, você pode escolher aleatoriamente entre a subárvore esquerda ou direita, com no máximo O (log2) etapas. Implementei uma demonstração dos itens abaixo abaixo:
Eu tenho [995, 975, 971, 995, 1057, 1004, 966, 1052, 984, 1001] como saída, então a distribuição parece boa.
Eu lutei com o mesmo problema para mim e ainda não decidi enfrentar o ganho de desempenho dessa escolha mais eficiente que vale a pena a sobrecarga do uso de uma coleção baseada em python. É claro que eu poderia refiná-lo e traduzi-lo para C, mas isso é muito trabalho para mim hoje :)
fonte