Как найти делители числа си шарп

tatarrr

0 / 0 / 0

Регистрация: 14.02.2013

Сообщений: 108

1

Как вывести все делители числа

25.06.2014, 13:24. Показов 26283. Ответов 17

Метки нет (Все метки)


Студворк — интернет-сервис помощи студентам

Вписываем число, должны быть выведены все его делители. Я написал:

C#
1
2
3
4
5
6
            Console.Write("Введи число ");
            int i = int.Parse(Console.ReadLine());
            for (int a = 1; a <= i; a++)
            {
                if (i % a == 0) Console.Write("{0} ", a);
            }

Я написал, но проблема в том, что это не совсем эффективно. То есть если число i делится на 2, то нет смысла проверять(как в моем методе) i/2, то есть допустим если число 12 делится на 2, то нет смысла проверять число 6, а надо сразу его вывести. Как это сделать?



0



31 / 30 / 13

Регистрация: 15.02.2014

Сообщений: 157

25.06.2014, 13:30

2

tatarrr, Мне кажется ваш способ наиболее эффективен. Только если с проверками и хранением данных о числах, которые уже имеются, можно избавиться от вашей проблемы



1



0 / 0 / 0

Регистрация: 14.02.2013

Сообщений: 108

25.06.2014, 13:33

 [ТС]

3

fast1kkk
К сожалению я решил эту задачу сегодня 3 разными способами, преподаватель на этот способ сказал, что не эффективно, а на остальные – ахинея((



0



31 / 30 / 13

Регистрация: 15.02.2014

Сообщений: 157

25.06.2014, 13:36

4

tatarrr, Можете показать ваши способы



0



Lexeq

1147 / 739 / 483

Регистрация: 21.01.2014

Сообщений: 1,903

25.06.2014, 13:51

5

C#
1
for (int a = 1; a <= i/2; a++)

Так как после i/2 результат будет лежать в пределах от 1 до 2, то нет смысла искать там целочисленные делители. Вообще, вроде бы нужно искать до sqrt(i), но я обычно делаю до i/2, ибо не силен в математике



1



993 / 891 / 354

Регистрация: 24.03.2014

Сообщений: 2,381

Записей в блоге: 2

25.06.2014, 13:55

6

И вот это стоит почитать, пожалуй…



2



sk007

Life Builder

532 / 496 / 374

Регистрация: 12.01.2011

Сообщений: 1,753

25.06.2014, 14:16

7

Цитата
Сообщение от tatarrr
Посмотреть сообщение

если число 12 делится на 2, то нет смысла проверять число 6, а надо сразу его вывести. Как это сделать?

может break; поставить

C#
1
2
3
4
5
6
            Console.Write("Введи число ");
            int i = int.Parse(Console.ReadLine());
            for (int a = 1; a <= i; a++)
            {
                if (i % a == 0) {Console.Write("{0} ", a); break;}
            }



1



Sergio Leone

2509 / 1130 / 582

Регистрация: 07.06.2014

Сообщений: 3,286

25.06.2014, 14:40

8

Может ваш преподаватель ожидал увидеть такой код:

C#
1
2
3
4
5
6
            Console.Write("Введи число ");
            int i = int.Parse(Console.ReadLine());
            for (int a = 1; a*a < i; a++)
            {
                if (i % a == 0) { Console.Write("{0} {1} ", a, i / a); }
            }

он выводит все делители числа, только в неотсортированном виде.



1



tatarrr

0 / 0 / 0

Регистрация: 14.02.2013

Сообщений: 108

25.06.2014, 17:01

 [ТС]

9

Sergio Leone
Ваша программа работает не всегда, например с числом 9.
Я написал кстати программу исходя из собственных доводов, вот код, как думаете, оценит?

C#
1
2
3
4
5
6
7
8
9
10
Console.Write("Введи число ");
            int i = int.Parse(Console.ReadLine());
            for (int a = 1; a <= i / a; a++)
            {
                if (i % a == 0)
                {
                    if (a != i / a) Console.Write("{0} {1} ", a, i / a);
                    else Console.Write("{0} ", a);
                }
            }

Этот код пишу, чтобы не было такого казуса, как при числе 9, выводится число 3 два раза

C#
1
2
 if (a != i / a) Console.Write("{0} {1} ", a, i / a);
                    else Console.Write("{0} ", a);



0



Psilon

Master of Orion

Эксперт .NET

6094 / 4950 / 905

Регистрация: 10.07.2011

Сообщений: 14,522

Записей в блоге: 5

25.06.2014, 17:11

10

tatarrr, просто правая граница включительно должна быть:

C#
1
2
3
4
5
6
7
            Console.Write("Введи число ");
            int n = int.Parse(Console.ReadLine());
            for (int i = 1; i * i <= n; i++)
            {
                if (n % i == 0)
                    Console.WriteLine(i);
            }

и выбирайте для циклом переменные i,j или k. Другие буквы лучше не использовать.



0



2509 / 1130 / 582

Регистрация: 07.06.2014

Сообщений: 3,286

25.06.2014, 19:26

11

Цитата
Сообщение от Psilon
Посмотреть сообщение

tatarrr, просто правая граница включительно должна быть:

сейчас нет возможности проверить код.
а что Ваш код выведет, например, для числа 17 ? А для числа 100 ?



0



Psilon

Master of Orion

Эксперт .NET

6094 / 4950 / 905

Регистрация: 10.07.2011

Сообщений: 14,522

Записей в блоге: 5

25.06.2014, 19:32

12

Sergio Leone, это простое число. Этот код возвращает все делители. Если нужно в конце и само число вывести, в конце можно просто так и сделатЬ:

C#
1
2
3
4
5
6
            for (int i = 1; i * i <= n; i++)
            {
                if (n % i == 0)
                    Console.WriteLine(i);
            }
            Console.WriteLine(n);



0



2509 / 1130 / 582

Регистрация: 07.06.2014

Сообщений: 3,286

25.06.2014, 21:20

13

да я вам о другом толкую.
введите, например, число 12. Оно же не простое?
ваш код, если я правильно понимаю, выведет
1
2
3
12

А куда делись делители
4
6
?!

А мой код с выводом ПАРЫ делителей выводил ВСЕ делители числа, как и требуется в задаче!

Нужно только не выводить пару, когда число N является квадратом натурального числа.
проверка на это
if (!(i==n/i)) вот тогда вывести пару делителей i и n/i

Добавлено через 2 минуты

Цитата
Сообщение от tatarrr
Посмотреть сообщение

Я написал кстати программу исходя из собственных доводов, вот код, как думаете, оценит?

О, точно так. Я именно такое и предлагал.
И если ваш препод не оценит это решение, тогда я не понимаю, что же он от Вас ждёт!

Отпишитесь после сдачи о результатах, плиз.



0



Master of Orion

Эксперт .NET

6094 / 4950 / 905

Регистрация: 10.07.2011

Сообщений: 14,522

Записей в блоге: 5

25.06.2014, 22:32

14

Sergio Leone, тогда берете ваш первый код и это будет самый быстрый вариант. Быстрее не бывает. Что еще хотите?..



1



2509 / 1130 / 582

Регистрация: 07.06.2014

Сообщений: 3,286

25.06.2014, 22:34

15

Psilon, дык, вся интрига в том, чего преподаватель добивается от автора темы.
Надеюсь, мы это узнаем после попытки сдачи кода.

я верю в то, что результат с i*i<=n препода полностью удовлетворит!



0



Psilon

Master of Orion

Эксперт .NET

6094 / 4950 / 905

Регистрация: 10.07.2011

Сообщений: 14,522

Записей в блоге: 5

25.06.2014, 22:48

16

Sergio Leone, может был вопрос про простые делители. Потому что до половины доже не вариант смотреть. Например у числа 6 делители 2 4 6, при этом 4 > 6/2.

Добавлено через 2 минуты
Sergio Leone, то есть судя по туманному описанию автора, препод просит его факторизовать число, а потом просто выводить множители, но это будет дольше, чем обычный перебор. Например:

Цитата
Сообщение от tatarrr
Посмотреть сообщение

То есть если число i делится на 2, то нет смысла проверять(как в моем методе) i/2, то есть допустим если число 12 делится на 2, то нет смысла проверять число 6, а надо сразу его вывести. Как это сделать?

то есть предлагается найти, что 2 делитель, а после этого

C#
1
2
for(int i = 2; i < n; i+=2)
   Console.WriteLine(i);

но это бред же. Ни о каком ускорении тут речь идти не может.

Добавлено через 42 секунды
tatarrr, короче, ваш препод фигню сказал. Перебор ВСЕХ делителей числа способом, который вы написали, самый быстрый. Можете ему показать эту тему



0



1147 / 739 / 483

Регистрация: 21.01.2014

Сообщений: 1,903

26.06.2014, 00:00

17

Цитата
Сообщение от Psilon
Посмотреть сообщение

Потому что до половины доже не вариант смотреть. Например у числа 6 делители 2 4 6, при этом 4 > 6/2.

4? 6/4 = 1.5



1



Master of Orion

Эксперт .NET

6094 / 4950 / 905

Регистрация: 10.07.2011

Сообщений: 14,522

Записей в блоге: 5

26.06.2014, 13:04

18

Lexeq, да, туплю



0



Формулировка задачи:

Вписываем число, должны быть выведены все его делители. Я написал:

            Console.Write("Введи число ");
            int i = int.Parse(Console.ReadLine());
            for (int a = 1; a <= i; a++)
            {
                if (i % a == 0) Console.Write("{0} ", a);
            }

Я написал, но проблема в том, что это не совсем эффективно. То есть если число i делится на 2, то нет смысла проверять(как в моем методе) i/2, то есть допустим если число 12 делится на 2, то нет смысла проверять число 6, а надо сразу его вывести. Как это сделать?

Код к задаче: «Как вывести все делители числа»

textual

            for (int i = 1; i * i <= n; i++)
            {
                if (n % i == 0)
                    Console.WriteLine(i);
            }
            Console.WriteLine(n);

Полезно ли:

9   голосов , оценка 4.000 из 5

I saw it would be better if I post an answer instead of using comments.

For your IsPrime method, I see that you’ve covered most conditions, but you forgot to cover 0, 1, and -n cases, which you can just do with a small change on this line :

if (n % 2 == 0) return false;

to

if (n < 2 || n % 2 == 0) return false;

suggested :

public static bool IsPrime(int n)
{
    if(n == 2) { return true; }

    if (n < 2 || (n % 2 == 0)) { return false; }

    for (int x = 3; x * x <= n; x += 2)
    {
        if (n % x == 0) { return false; }
    }

    return true;
}

For GetDivisors. I assume that you excluded 1 and n from the results, since it’s already known that every natural number is divisible by 1 and itself, which is fine if you intended to use this code personally, but it is uncommon to do that, and it might even conflict with other developers code, as you don’t want to assume everybody knows that! so it must be included to make the code more usable for others, and you must always consider what is common use, and what is not.

The technique in @gazoh answer is a really good one, and I would take it to the next level, but I have to note out that Sort and ToArray are expensive operations as I’ve mentioned in the comments. And I would avoid using them directly.

I’ve updated this method to :

private static IEnumerable<int> GetDivisors(int n)
{
    if (n <= 0) { yield return default; }

    int iterator = (int)Math.Sqrt(n);

    for (int i = 1; i <= iterator; i++)
    {
        if (n % i == 0)
        {
            yield return i;                    

            if (i != n / i) { yield return n / i; }
        }
    }
}

public static IEnumerable<int> GetDivisors(int n, bool AscendingOrder = false)
{
    return !AscendingOrder ? GetDivisors(n) : GetDivisors(n).OrderBy(x => x);
}

I’ve used IEnumerable<int> to open more acceptable collections types rather than just sticking with an array. The overload is just to have an option to order the data, while the default is unordered data. This would make it optional, which would depend on usage, if you prefer performance over order, or order over performance. Then you can convert it to list or array or any collection.

For performance differences, I’ve used BenchmarkDotNet to test and compare this method performance and here is the results.

Test 1 using GetDivisors(2095133040)

|             Method |                 Mean |               Error |              StdDev |
|------------------- |---------------------:|--------------------:|--------------------:|
|           Original | 5,907,229,864.000 ns | 116,153,274.6298 ns | 155,061,285.3568 ns |
|            ByGazoh |       169,411.783 ns |       2,805.6563 ns |       2,487.1413 ns |
|   IEnumerable<int> |             6.637 ns |           0.1896 ns |           0.2107 ns |
|            OrderBy |            17.514 ns |           0.3013 ns |           0.2516 ns |
|            ToArray |       151,408.141 ns |       2,906.1875 ns |       2,854.2648 ns |
|             ToList |       363,424.079 ns |       5,318.8335 ns |       4,975.2401 ns |
|  ToArray + OrderBy |       154,249.309 ns |       2,370.8673 ns |       2,101.7121 ns |
|   ToList + OrderBy |       356,705.127 ns |       6,002.7773 ns |       5,321.3057 ns |

Test 2 using GetDivisors(1600)

|             Method |         Mean |      Error |     StdDev |
|------------------- |-------------:|-----------:|-----------:|
|           Original | 4,804.474 ns | 93.0708 ns | 91.4080 ns |     
|           ByGazoh  |   515.822 ns |  7.6545 ns |  7.1601 ns |     
|   IEnumerable<int> |     6.391 ns |  0.0966 ns |  0.0904 ns |     
|            OrderBy |    16.783 ns |  0.2839 ns |  0.2517 ns |     
|            ToArray |   422.570 ns |  6.3368 ns |  5.9274 ns |     
|             ToList |   463.575 ns |  8.0975 ns |  7.5744 ns |
|  ToArray + OrderBy | 1,662.728 ns | 26.8204 ns | 25.0878 ns |
|   ToList + OrderBy | 1,634.595 ns | 30.9492 ns | 28.9499 ns |

ns = nano-second;

Where

  • OrderBy = Divisors.GetDivisors(n).OrderBy(x=>x);
  • ToArray = Divisors.GetDivisors(n).ToArray();
  • ToList = Divisors.GetDivisors(n).ToList();
  • ToArray + OrderBy = Divisors.GetDivisors(n).OrderBy(x=>x).ToArray();
  • ToList + OrderBy =Divisors.GetDivisors(n).OrderBy(x=>x).ToList();

Lastly, regarding your tests, I suggest you test each scenario separately.

Example :

[TestMethod]
public void GetDivisors_15_IsEqual()
{
    Assert.AreEqual(new int[] { 3, 5 }, Divisors.Divisors(15));
}

[TestMethod]
public void GetDivisors_16_IsEqual()
{
    Assert.AreEqual(new int[] { 2, 4, 8 }, Divisors.Divisors(16));
}

[TestMethod]
public void GetDivisors_253_IsEqual()
{
    Assert.AreEqual(new int[] { 11, 23 }, Divisors.Divisors(253));
}

[TestMethod]
public void GetDivisors_24_IsEqual()
{
    Assert.AreEqual(new int[] { 2, 3, 4, 6, 8, 12 }, Divisors.Divisors(24));
}

 the reason is simple, if you create a separate test for each scenario you have, it’ll be easy to determine which part of your code needs adjustments, and it would make things easier for improving your code (say you want to simplify it without breaking the code). Also, it would be more easier to read and follow, and could give you a better view on the requirements, and the validation process of it.

If you don’t detailed your tests, in smaller projects you might not have any issues, but in big projects, it’ll be a pain in the neck.

I hope this would be useful.

Практикуюсь на Codewars. Написал метод поиска делителей числа. Он проходитит все тесты, кроме Performance Random Test. Получил System.OutOfMemoryException. Что с этим можно сделать? Заранее, спасибо!

public static int[] Divisors(int n)
{
    int N = 0;
    int[] divisors = new int[n];
    for (int i = 2; i < n; i++)
    {
        if (n % i == 0)
        {
            divisors[i] = i;
            N++;
        }
    }

    int[] ar = new int[N];
    int j = 0;
    for (int i = 0; i < divisors.Length; i++)
    {
        if (divisors[i] != 0)
        {
            ar[j] = divisors[i];
            j++;
        }
        else if (ar.Length == 0)
        {
            return null;
        }
    }
    return ar;
}

Andrei Khotko's user avatar

задан 9 фев 2021 в 11:39

Sinister's user avatar

5

  1. List<int> – лучше решение, когда вы не знаете, сколько будет элементов в коллекции.
  2. Число не может делиться на что-то большее, чем половина этого числа (верно?), следовательно перебор уже можно вести только до n / 2
  3. Еще круче, если перебор проводить только до квадратного корня числа, при этом добавляя и делитель и частное, так как от перестановки множителей местами произведение не меняется.

Но я думаю и то что я написал, можно еще улучшить. Это так, наивная реализация:

public static int[] Divisors(int n)
{
    List<int> divisors = new List<int>();
    for (int i = 2; i * i <= n; i++)
    {
        if (n % i == 0)
        {
            divisors.Add(i);
            if (i * i != n)
                divisors.Add(n / i);
        }
    }
    divisors.Sort(); // это для красивого вывода, но можно убрать в пользу производительности
    return divisors.ToArray();
}

Еще можно не делать .ToArray(), а сделать вот так

public static List<int> Divisors(int n)
{
    //...
    return divisors;
}

Но это зависит от того, обязательное у вас условие вернуть именно массив, или просто так получилось.

ответ дан 9 фев 2021 в 12:01

aepot's user avatar

aepotaepot

45k5 золотых знаков22 серебряных знака50 бронзовых знаков

5

Как найти количество делителей числа.

Например число

20 -> 1 2 4 5 10 20

1 -> 1 = 1

2 -> 1, 2 = 2

4 -> 1, 2, 4 = 3

5 -> 1, 2 = 2

10 -> 1, 2, 5, 10 = 4

20 -> 1, 2, 4, 5, 10, 10 = 6

1 + 2 + 3 + 2 + 4 + 6 = 18

Harry

144k10 золотых знаков80 серебряных знаков179 бронзовых знаков

задан 3 сен ’16 в 5:31

Откровенно говоря, ничего умнее, чем перебор простых делителей числа до sqrt(N) не вижу. Ну, а потом – перебор сочетаний этих простых делителей в составные делители. Понятно, что при нахождении простого делителя делим число на него и начинаем все сначала. И не менее понятно, что количество всех сочетаний (== количество делителей) есть просто произведение всех степеней простых делителей, увеличенные на 1. Ну, например, 360 = 2^3 * 3^2 * 5^1, так что число делителей (3+1)*(2+1)*(1+1)=24.

Если числа небольшие – можно просто перебор всех подряд делителей до sqrt(N), с учетом, что для каждого такого делителя, отличного от 1, есть соответствующий делитель с обратной стороны от sqrt(N).

Код нужен? 🙂

Update: пример – вывод количества всех делителей (включая 1 и само число)

ответ дан 3 сен ’16 в 7:21

HarryHarry

144k10 золотых знаков80 серебряных знаков179 бронзовых знаков

Можно, найти алгоритм и немного получше, чем перебор простых делителей числа до sqrt(N).

Обратим внимание на тот факт, что любое число, можно представить как произведение простых множителей, таким образом если мы нашли один простой делитель, то далее нам не надо искать делители N, а надо искать делители от частного N/x, где х – наш простой делитель.

Например так. (Извиняюсь за C#)

uint[] simpl_arr = //массив заполненный простыми числами

public List<int> GetSimpleDividors(uint number)
{
    List<int> result = new List<int>();
    uint s = Convert.ToUInt32(Math.Ceiling(Math.Sqrt(number)));

    for ( int i = 0 ; simple_arr[i] < s ; i++ ) 
    {
        if (number % simple_arr[i] == 0)
        {
            result.Add(simple_arr[i]);
            uint quotient = number / simple_arr[i]; 

            var tmp = GetSimpleDividors(quotent);

            if (tmp.Count > 0)
            {
                 result.AddRange(tmp);
            }
            else
            {
                 result.Add(quotent);
            }
            break;
        }
    }

    return result;
}

ответ дан 3 сен ’16 в 12:27

MirdinMirdin

5,7421 золотой знак18 серебряных знаков29 бронзовых знаков

Можно в цикле через алгоритм Евклида считать до деления на 0.
До конца цикла проверять результат деления: если остаток от деления равен 0, то прибавить 1 к счётчику.
В счётчике считаем количество делителей.

ответ дан 5 июн ’18 в 21:20

XalionXalion

361 бронзовый знак

Всё ещё ищете ответ? Посмотрите другие вопросы с метками c++ алгоритм математика или задайте свой вопрос.

Добавить комментарий