Digamos que temos 0.33
, precisamos produzir 1/3
.
Se tivermos 0.4
, precisamos produzir 2/5
.
A ideia é torná-lo legível para que o usuário entenda " x partes de y " como uma maneira melhor de entender os dados.
Eu sei que as porcentagens são um bom substituto, mas gostaria de saber se existe uma maneira simples de fazer isso?
algorithm
language-agnostic
numbers
Swaroop CH
fonte
fonte
.33
=>"1/3"
exemplo me preocupa; Eu esperaria.33
=>"33/100"
. Suponho que você quisesse dizer.33...
, é claro, mas isso expõe um problema com a questão - antes de podermos definir um algoritmo, precisamos decidir sobre o comportamento esperado. A resposta de @Debilski em Python usa o.limit_denominator()
padrão para um denominador máximo de 10 ^ 7; provavelmente um bom padrão na prática, mas isso ainda pode introduzir erros se você não tiver cuidado, e faz o retorno"33/100"
no.33
caso.Respostas:
Eu descobri que a aproximação racional encontrada de David Eppstein para um determinado código C de número real é exatamente o que você está pedindo. É baseado na teoria das frações contínuas e muito rápido e bastante compacto.
Usei versões customizadas para limites específicos de numerador e denominador.
fonte
Do Python 2.6 em diante, existe o
fractions
módulo.(Citando os documentos.)
fonte
language agnostic
ealgorithm
tags sua resposta satisfaz?Se a saída é para dar a um leitor humano uma impressão rápida da ordem do resultado, não faz sentido retornar algo como "113/211", então a saída deve se limitar a usar números de um dígito (e talvez 1 / 10 e 9/10). Nesse caso, você pode observar que existem apenas 27 frações diferentes .
Como a matemática subjacente para gerar a saída nunca mudará, uma solução poderia ser simplesmente codificar permanentemente uma árvore de pesquisa binária, de modo que a função executasse no máximo log (27) ~ = 4 3/4 comparações. Aqui está uma versão C testada do código
fonte
1/1000
também é muito legível, mas o algoritmo acima produziria apenas uma1/10
aproximação muito grosseira ; Acredito que melhorias podem ser feitas em termos da qual denominadores humanamente legíveis um pode escolher, e / ou a adição de<
,>
,<<
,>>
prefixos para dar uma idéia da grossura do aproximação.Aqui está um link que explica a matemática por trás da conversão de um decimal em uma fração:
http://www.webmath.com/dec2fract.html
E aqui está um exemplo de função de como realmente fazer isso usando VB (em www.freevbcode.com/ShowCode.asp?ID=582):
(A partir de pesquisas do google: converta decimal em fração, converta decimal em código de fração)
fonte
Você pode querer ler O que todo cientista da computação deve saber sobre aritmética de ponto flutuante .
Você terá que especificar alguma precisão multiplicando por um grande número:
então você pode fazer uma fração:
e reduzir via GCD ...
mas não há como retirar a fração pretendida . Você pode querer sempre usar frações em todo o seu código - apenas lembre-se de reduzir as frações quando puder para evitar o estouro!
fonte
Aqui estão as versões Perl e Javascript do código VB sugerido por devinmoore:
Perl:
E o javascript quase idêntico:
fonte
Implementação AC #
fonte
A Árvore Stern-Brocot induz uma maneira bastante natural de aproximar os números reais por frações com denominadores simples.
fonte
Parte do problema é que muitas frações não são realmente facilmente interpretadas como frações. Por exemplo, 0,33 não é 1/3, é 33/100. Mas se você se lembrar de seu treinamento na escola primária, então há um processo de conversão de valores decimais em frações. No entanto, é improvável que você obtenha o que deseja, já que na maioria das vezes os números decimais não são armazenados em 0,33, mas em 0,329999999999998 ou algo parecido.
Faça um favor a si mesmo e não se preocupe com isso, mas se precisar, você pode fazer o seguinte:
Multiplique o valor original por 10 até remover a parte fracionária. Mantenha esse número e use-o como divisor. Em seguida, faça uma série de simplificações procurando denominadores comuns.
Portanto, 0,4 seria 4/10. Em seguida, você procuraria divisores comuns começando com valores baixos, provavelmente números primos. Começando com 2, você veria se 2 divide o numerador e o denominador igualmente, verificando se o piso da divisão é o mesmo da própria divisão.
Portanto, 5 não divide 2 igualmente. Então você verifica o próximo número, digamos 3. Faça isso até atingir a raiz quadrada ou acima do número menor.
Depois de fazer isso, você precisa
fonte
Este não é um "algoritmo", apenas uma solução Python: http://docs.python.org/library/fractions.html
fonte
"Digamos que temos 0,33, precisamos produzir" 1/3 "."
Que precisão você espera que a "solução" tenha? 0,33 não é igual a 1/3. Como você reconhece uma resposta "boa" (fácil de ler)?
Não importa o que aconteça, um algoritmo possível poderia ser:
Se você espera encontrar a fração mais próxima na forma X / Y onde Y é menor que 10, então você pode fazer um loop por todos os 9 Ys possíveis, para cada Y computar X, e então selecionar o mais preciso.
fonte
Acho que a melhor maneira de fazer isso é primeiro converter seu valor float em uma representação ASCII. Em C ++ você pode usar ostringstream ou em C, você pode usar sprintf. Veja como ficaria em C ++:
Uma abordagem semelhante poderia ser adotada em linha reta C.
Depois disso, você precisará verificar se a fração está em termos mais baixos. Este algoritmo fornecerá uma resposta precisa, ou seja, 0,33 produziria "33/100", não "1/3". No entanto, 0,4 daria "4/10", que quando reduzido aos termos mais baixos seria "2/5". Isso pode não ser tão poderoso quanto a solução de EppStein, mas acredito que seja mais simples.
fonte
Uma solução integrada em R:
Este utiliza um método de fração contínua e tem opcional
cycles
emax.denominator
argumentos para ajustar a precisão.fonte
library(numbers)
econtFrac(0.6666)
; para obter a saída da string conforme desejado:paste(contFrac(0.666, tol=1e-03)$rat, collapse="/")
Você terá que descobrir que nível de erro está disposto a aceitar. Nem todas as frações decimais se reduzirão a uma fração simples. Eu provavelmente escolheria um número facilmente divisível, como 60, e descobriria quantos 60 está mais próximo do valor e, em seguida, simplificaria a fração.
fonte
Você pode fazer isso em qualquer linguagem de programação usando as seguintes etapas:
Exemplo: 0,2 = 0,2 x 10 ^ 1/10 ^ 1 = 2/10 = 1/5
Então, isso pode ser lido como '1 parte de 5'
fonte
Uma solução é simplesmente armazenar todos os números como números racionais em primeiro lugar. Existem bibliotecas para aritmética de números racionais (por exemplo, GMP ). Se estiver usando uma linguagem OO, você poderá usar apenas uma biblioteca de classes de números racionais para substituir sua classe de números.
Os programas de finanças, entre outros, usariam essa solução para fazer cálculos exatos e preservar a precisão que pode ser perdida com o uso de um float simples.
Claro que será muito mais lento, por isso pode não ser prático para você. Depende de quantos cálculos você precisa fazer e da importância da precisão para você.
fonte
É errado no caso comum, porque 1/3 = 0,33333333 = 0. (3) Além disso, é impossível descobrir a partir das soluções sugeridas acima se o decimal pode ser convertido em fração com precisão definida, porque a saída é sempre fração.
MAS, eu sugiro minha função abrangente com muitas opções baseadas na ideia de séries geométricas infinitas , especificamente na fórmula:
A princípio, essa função tenta encontrar o período da fração na representação da string. Depois disso, a fórmula descrita acima é aplicada.
O código de números racionais foi emprestado da implementação de números racionais de Stephen M. McKamey em C #. Espero que não seja muito difícil portar meu código para outras linguagens.
Existem alguns exemplos de usos:
Seu caso com recorte da parte zero da parte direita:
Demonstração de período mínimo:
Arredondamento no final:
O caso mais interessante:
Outros testes e códigos que todos podem encontrar em minha biblioteca MathFunctions no github .
fonte
Ruby já tem uma solução integrada:
No Rails, os atributos numéricos ActiveRecord também podem ser convertidos:
fonte
Responda em C ++, supondo que você tenha uma classe 'BigInt', que pode armazenar inteiros de tamanho ilimitado.
Você pode usar 'unsigned long long' em vez disso, mas só funcionará para certos valores.
BTW, GetRational (0.0) retornará "+0/1", então você pode querer lidar com este caso separadamente.
PS: Tenho usado esse código em minha própria classe 'RationalNum' por vários anos e ele foi testado exaustivamente.
fonte
while
loop é limitado pelo tamanho dedouble
, que normalmente é de 64 bits. Portanto, não depende do valor inicial da entrada (val
). AGCD
função, no entanto, depende desse valor, embora geralmente converta para uma solução muito rápida. É possível que você não implementou essa função corretamente?unsigned long long
vez deBigInt
, não necessariamente produzirá o resultado correto para cada valor de entrada ... Mas mesmo nesse cenário, o código não é deveria "entrar em um loop muito longo".GCD
função não seja implementada corretamente. Você verificou se o código é executado por muito tempo durante owhile
loop ou depois dele? Vou verificar o valor de 1,33333, para ver o que está por trás disso. Obrigado.Este algoritmo de Ian Richards / John Kennedy não só retorna boas frações, mas também tem um desempenho muito bom em termos de velocidade. Este é o código C # retirado desta resposta minha.
Ele pode lidar com todos os
double
valores, exceto valores especiais como NaN e +/- infinito, que você terá que adicionar se necessário.Ele retorna a
new Fraction(numerator, denominator)
. Substitua pelo seu próprio tipo.Para obter mais valores de exemplo e uma comparação com outros algoritmos, clique aqui
Valores de exemplo retornados por este algoritmo:
fonte
Você terá dois problemas básicos que tornarão isso difícil:
1) O ponto flutuante não é uma representação exata, o que significa que se você tiver uma fração de "x / y" que resulta em um valor de "z", seu algoritmo de fração pode retornar um resultado diferente de "x / y".
2) Existem infinitos, muitos mais números irracionais do que racionais. Um número racional é aquele que pode ser representado como uma fração. Ser irracionais que não podem.
No entanto, de uma forma barata, uma vez que o ponto flutuante tem precisão limite, você sempre pode representá-lo como alguma forma de facção. (Eu acho que...)
fonte
Concluiu o código acima e o converteu em as3
fonte
Aqui está uma implementação rápida e suja em javascript que usa uma abordagem de força bruta. Nem um pouco otimizado, ele funciona dentro de um intervalo predefinido de frações: http://jsfiddle.net/PdL23/1/
Isso é inspirado na abordagem usada pelo JPS.
fonte
Como muitas pessoas afirmaram, você realmente não pode converter um ponto flutuante de volta em uma fração (a menos que seja extremamente exato, como 0,25). Claro que você pode criar algum tipo de pesquisa para um grande array de frações e usar algum tipo de lógica difusa para produzir o resultado que está procurando. Novamente, isso não seria exato e você precisaria definir um limite inferior de quão grande você deseja que o denominador vá.
0,32 <x <0,34 = 1/3 ou algo parecido.
fonte
Aqui está a implementação para ruby http://github.com/valodzka/frac
fonte
Me deparei com uma solução Haskell especialmente elegante que faz uso de um anamorfismo. Depende do pacote de esquemas de recursão .
Se você experimentar no ghci, realmente funciona!
fonte