c# - numeros - La mejor forma de encontrar todos los factores de un número determinado
factores primos (10)
Todos los números que se dividen equitativamente en x.
Puse 4 que regresa: 4, 2, 1
editar: Sé que suena muy casero. Estoy escribiendo una pequeña aplicación para completar algunas tablas de productos con datos de prueba semialeatorios. Dos de las propiedades son ItemMaximum y Item Multiplier. Necesito asegurarme de que el multiplicador no cree una situación ilógica donde comprar 1 artículo más pondría la orden por encima del máximo permitido. Por lo tanto, los factores darán una lista de valores válidos para mis datos de prueba.
editar ++: Esto es lo que hice después de toda la ayuda de todos. ¡Gracias de nuevo!
edición #: escribí 3 versiones diferentes para ver cuál me gustaba más y las probé contra factorizar números pequeños y números muy grandes. Pegaré los resultados.
static IEnumerable<int> GetFactors2(int n)
{
return from a in Enumerable.Range(1, n)
where n % a == 0
select a;
}
private IEnumerable<int> GetFactors3(int x)
{
for (int factor = 1; factor * factor <= x; factor++)
{
if (x % factor == 0)
{
yield return factor;
if (factor * factor != x)
yield return x / factor;
}
}
}
private IEnumerable<int> GetFactors1(int x)
{
int max = (int)Math.Ceiling(Math.Sqrt(x));
for (int factor = 1; factor < max; factor++)
{
if(x % factor == 0)
{
yield return factor;
if(factor != max)
yield return x / factor;
}
}
}
En tics. Al factorizar el número 20, 5 veces cada uno:
- GetFactors1-5,445,881
- GetFactors2-4,308,234
- GetFactors3-2,913,659
Al factorizar el número 20000, 5 veces cada uno:
- GetFactors1-5,644,457
- GetFactors2-12,117,938
- GetFactors3-3,108,182
¿No tendría sentido también comenzar en 2 y dirigirse hacia un valor límite superior que continuamente se recalcula según el número que acabas de marcar? Vea N / i (donde N es el número que está tratando de encontrar el factor de y i es el número actual para verificar ...) Idealmente, en lugar de mod, usaría una función de división que también devuelve N / i como cualquier resto que pueda tener. De esa forma estás realizando una operación dividida para recrear tu límite superior, así como el resto verificará la división par.
Math.DivRem http://msdn.microsoft.com/en-us/library/wwc1t3y1.aspx
Aquí está de nuevo, solo contando a la raíz cuadrada, como otros mencionaron. Supongo que la gente se siente atraída por esa idea si espera mejorar el rendimiento. Prefiero escribir un código elegante primero, y optimizar el rendimiento más adelante, después de probar mi software.
Aún así, como referencia, aquí está:
public static bool Divides(this int potentialFactor, int i)
{
return i % potentialFactor == 0;
}
public static IEnumerable<int> Factors(this int i)
{
foreach (int result in from potentialFactor in Enumerable.Range(1, (int)Math.Sqrt(i))
where potentialFactor.Divides(i)
select potentialFactor)
{
yield return result;
if (i / result != result)
{
yield return i / result;
}
}
}
No solo el resultado es considerablemente menos legible, sino que los factores también se vuelven anormales.
Como métodos de extensión:
public static bool Divides(this int potentialFactor, int i)
{
return i % potentialFactor == 0;
}
public static IEnumerable<int> Factors(this int i)
{
return from potentialFactor in Enumerable.Range(1, i)
where potentialFactor.Divides(i)
select potentialFactor;
}
Aquí hay un ejemplo de uso:
foreach (int i in 4.Factors())
{
Console.WriteLine(i);
}
Tenga en cuenta que he optimizado para mayor claridad, no para el rendimiento. Para valores grandes de i
este algoritmo puede llevar mucho tiempo.
El operador %
(remainder) es el que debe usarse aquí. Si x % y == 0
entonces x
es divisible por y
. (Suponiendo 0 < y <= x
)
Personalmente implementaría esto como un método para devolver un IEnumerable<int>
usando un bloque iterador.
Lo hice de manera perezosa. No sé mucho, pero me han dicho que la simplicidad a veces puede implicar elegancia. Esta es una forma posible de hacerlo:
public static IEnumerable<int> GetDivisors(int number)
{
var searched = Enumerable.Range(1, number)
.Where((x) => number % x == 0)
.Select(x => number / x);
foreach (var s in searched)
yield return s;
}
EDITAR : como señaló Kraang Prime, esta función no puede exceder el límite de un número entero y (por cierto) no es la forma más eficiente de manejar este problema.
Muy tarde, pero la respuesta aceptada (hace un tiempo) no dio los resultados correctos.
Gracias a Merlyn, ahora obtuve el motivo del cuadrado como un "máximo" debajo de la muestra corregida. aunque la respuesta de Echostorm parece más completa.
public static IEnumerable<uint> getFactors(uint x)
{
for (uint i = 1; i*i <= x; i++)
{
if (0 == (x % i))
{
yield return i;
if (i != (x / i))
{
yield return x / i;
}
}
}
}
Otro estilo LINQ y vincular para mantener la complejidad O (sqrt (n))
static IEnumerable<int> GetFactors(int n)
{
Debug.Assert(n >= 1);
var pairList = from i in Enumerable.Range(1, (int)(Math.Round(Math.Sqrt(n) + 1)))
where n % i == 0
select new { A = i, B = n / i };
foreach(var pair in pairList)
{
yield return pair.A;
yield return pair.B;
}
}
Si usa dobles, lo siguiente funciona: use un bucle for para iterar desde 1 hasta el número que desea factorizar. En cada iteración, divida el número que será factorizado por i. Si (número / i)% 1 == 0, entonces yo es un factor, como lo es el cociente de número / i. Coloque uno o ambos en una lista, y usted tiene todos los factores.
Solución Linq:
IEnumerable<int> GetFactors(int n)
{
Debug.Assert(n >= 1);
return from i in Enumerable.Range(1, n)
where n % i == 0
select i;
}
pseudocódigo:
- Pasa de 1 a la raíz cuadrada del número, llama al índice "i".
- Si el número mod i es 0, agregue i y number / i a la lista de factores.
realocode:
public List<int> Factor(int number) {
List<int> factors = new List<int>();
int max = (int)Math.Sqrt(number); //round down
for(int factor = 1; factor <= max; ++factor) { //test from 1 to the square root, or the int below it, inclusive.
if(number % factor == 0) {
factors.Add(factor);
if(factor != number/factor) { // Don''t add the square root twice! Thanks Jon
factors.Add(number/factor);
}
}
}
return factors;
}
Como mencionó Jon Skeet, también podría implementar esto como un IEnumerable<int>
: use el yield lugar de agregarlo a una lista. La ventaja con List<int>
es que se puede ordenar antes de devolver si es necesario. Por otra parte, podría obtener un enumerador ordenado con un enfoque híbrido, obteniendo el primer factor y almacenando el segundo en cada iteración del ciclo, y luego arrojando cada valor almacenado en orden inverso.
También querrá hacer algo para manejar el caso donde un número negativo pasó a la función.