Эффективно найти все делители числа
Поэтому я просто хочу найти все делители данного числа (за исключением самого числа). В настоящее время у меня есть это:
public static List<int> proper_divisors(int x)
{
List<int> toreturn = new List<int>();
toreturn.Add(1);
int i = 0;
int j=1;
int z = 0;
while (primes.ElementAt(i) < Math.Sqrt(x))
{
if (x % primes.ElementAt(i) == 0)
{
toreturn.Add(primes.ElementAt(i));
toreturn.Add(x / primes.ElementAt(i));
j = 2;
z = (int)Math.Pow(primes.ElementAt(i), 2);
while (z < x)
{
if (x % z == 0)
{
toreturn.Add(z);
toreturn.Add(x / z);
j++;
z = (int)Math.Pow(primes.ElementAt(i), j);
}
else
{
z = x;
}
}
}
i++;
}
toreturn = toreturn.Distinct().ToList<int>();
return toreturn;
}
где простые числа - это список простых чисел (предположим, что это правильно и достаточно велико). Алгоритм работает в том смысле, что он находит все основные факторы, но не все факторы (то есть, учитывая 34534, он возвращает {1,2,17267,31,1114}, но пропускает {62, 557}, поскольку 62 является комбинацией, и поэтому пропускает 557 также.
Я также пытался просто получить главные факторы числа, но я не знаю, как преобразовать это в список всех правильных комбинаций.
Код для этого алгоритма выглядит следующим образом:
public static List<int> prime_factors(int x)
{
List<int> toreturn = new List<int>();
int i = 0;
while (primes.ElementAt(i) <= x)
{
if (x % primes.ElementAt(i) == 0)
{
toreturn.Add(primes.ElementAt(i));
x = x / primes.ElementAt(i);
}
else
{
i++;
}
}
return toreturn;
}
Любые идеи о том, как исправить первый или как создать список комбинаций из второго (я бы предпочел, чтобы это было быстрее)?
2 ответа
Поскольку у вас уже есть список основных факторов, вы хотите вычислить набор параметров этого списка.
Теперь одна проблема состоит в том, что у вас могут быть дубликаты в списке (например, простые множители 20 = 2 * 2 * 5), но наборы не допускают дубликатов. Таким образом, мы можем сделать каждый элемент списка уникальным, проецируя его на структуру вида {x, y}, где x - простое число, а y - индекс простого числа в списке.
var all_primes = primes.Select((x, y) => new { x, y }).ToList();
Сейчас, all_primes
список вида {x, y}, где x - простое число, а y - индекс в списке.
Затем мы вычисляем набор мощности (определение GetPowerSet
ниже):
var power_set_primes = GetPowerSet(all_primes);
Следовательно, power_set_primes
является IEnumerable<IEnumerable<T>>
где T
это анонимный тип {x, y}
где х и у имеют тип int
,
Далее мы вычисляем произведение каждого элемента в наборе мощности
foreach (var p in power_set_primes)
{
var factor = p.Select(x => x.x).Aggregate(1, (x, y) => x * y);
factors.Add(factor);
}
Собираем все вместе:
var all_primes = primes.Select((x, y) => new { x, y }).ToList(); //assuming that primes contains duplicates.
var power_set_primes = GetPowerSet(all_primes);
var factors = new HashSet<int>();
foreach (var p in power_set_primes)
{
var factor = p.Select(x => x.x).Aggregate(1, (x, y) => x * y);
factors.Add(factor);
}
С http://rosettacode.org/wiki/Power_Set для реализации powerset.
public IEnumerable<IEnumerable<T>> GetPowerSet<T>(List<T> list)
{
return from m in Enumerable.Range(0, 1 << list.Count)
select
from i in Enumerable.Range(0, list.Count)
where (m & (1 << i)) != 0
select list[i];
}
Ранее был похожий вопрос, который имеет интересное решение с использованием IEnumerable. Если вам нужны все делители, а не факторы, и если вы используете хотя бы C# 3.0, вы можете использовать что-то вроде этого:
static IEnumerable<int> GetDivisors(int n)
{
return from a in Enumerable.Range(2, n / 2)
where n % a == 0
select a;
}
и затем используйте это так:
foreach(var divisor in GetDivisors(10))
Console.WriteLine(divisor);
или, если вы хотите список, просто:
List<int> divisors = GetDivisors(10).ToList();