Помощ за една програма на C++

Публикувана на: 11.03.2010, от imho
Кoментари:13

Става дума за задача "Нули" от Арена 2 , бронзовата дивизия, http://cs.maycamp.com .

Програмата трябва да намери по число N<50000 броя на нулите, с които завършва произведението на числата от 1 до N и последната му ненулева цифра.

Например за N=12 произведението е 479001600, има две завършващи нули и последната ненулева цифра е 6.

Ето моментното ми решение.
    #include<iostream>
using namespace std;

int main() {
int n, i, t, c2, c5, p;
c2 = 0;
c5 = 0;
p = 1;
cin >> n;
for (i = 2; i <= n; i++) {
t = i;
while (!(t % 5)) {
t /= 5;
c5++;
}
while (!(t % 2)) {
t /= 2;
c2++;
}
p *= t;
p %= 10;
}
for (i=0;i<c2-c5;i++){
p*=2;
p%=10;
}
cout << p << ' ' << c5 << endl;
}

Предложете нещо по-оптимизирано..

Коментари 13

05.11.2010 lexx

Profile

Не се заглеждам много,з ащото и аз съм на работа и нямам кой знае какво време. Иначе като се замисля има някой неща които могат да продобраят бързината на решението. Тоест може да се разглеждат всички числа завършващи на 1,3,7,9, които се намират във всеки интервал кратен на 10.  0-10, 10-20, 20-30, ... От произведението им 1*3*7*9* = 189 следва че последната цифра е 9. От което веднага се прави извода, че ако имам интервал 1-100 => последната цифра на произжедението на всички числа завършващи 1,3,7,9 е 9^10. Ако се разгледа че 9*9 = 81 => 9^2к => 1 за последната цифра, а 9^к=> 9 за последната цифра, където к е нечетно естествено чивсло;

Разбира се може да се помисли и от другите действия да се намали малко, а и може да се пробват и дребни подобрения за бързодействието като << 2 , вместо деление на 2.

04.11.2010 imho

Profile
long long - 2^63 ~ 9.10^18 - достатъчно е.

t=4-c5%4;

В този момент имам да разделя c5 пъти на 2. При това съм сигурен, че последната цифра е четна, защото двойките са много повече от петиците. Както написах, при 4 пъти умножение по 2 на четно число последната цифра се повтаря - 2,4,8,6,2,4,8,6.... т.е. няма нужда да разделя c5 пъти на 2, а само c5%4. Не мога да деля, защото нямам цялото число, а само края му, затова умножавам, докато стигна в обратния ред пак до същата последна цифра (4-c5%4) пъти...Малко може да е объркващо, но трябва да работи.

Нещо такова имам предвид, което да намали изчисленията...тя задачата е супер елементарна и тривиалното решение няма проблем, просто в него момент реших да търся някакъв по-бърз начин за изчисление на последната цифра. Иначе за броя на нулите има и по-цивилизовано решение - дели се там n целочислено на 5, на 5, на 5 и се събират...

03.11.2010 lexx

Profile

 Извинявам, сега след като по-подробно загледах предложеното решение, на христов и след като забелязах типа long long, смятам че ще работи правилно, защото се съмнявам да се получи толкоз голямо p, че да се получи препълване - не съм го смятал, но в найлошия случай ще има умножение на 6 ицфрено число по 5 цифрено такова.

03.11.2010 lexx

Profile

Ами като гледам така задачата е на прав път, само това накрая не ми се връзва

    t=4-c5%4;
for (i=0; i<t; i++)
Не знам защо така намираш цифрата преди нулите?
А доколкото погледнах мисля, че решението на Христов не е вярно.
Разбира се може да се сравнят за някое голямо число над 1000 разбирам. 

 

28.10.2010 imho

Profile

По-скоро имах предвид нещо такова:

Когато последователно умножаваме едно число по 2, забелязваме, че
последна цифра се редува: 2, 4, 8, 6, 2, 4, 8, 6. Т.е. можем да си
спестим малко умножения:

#include<cstdio>

int main() {
int n, i, t, c5, p;
c5 = 0;
p = 1;
scanf("%d",&n);
    for (i = 2; i <= n; i++) {
t = i;
while (!(t % 5)) {
t /= 5;
c5++;
}
p *= t;
p%=10;
}

t=4-c5%4;
for (i=0; i<t; i++) {
p*=2;
}

p%=10;
printf("%d %d\n", p, c5);
}

Решението на Христов ми харесва, по-директно е, нямам идея кое ще е по-бързо, все пак то работи с 64-битови числа...

А за другата задача стигнах до идеята - scanf(&a[ i][ 1]).

26.10.2010 lexx

Profile

 Тук все пак искам да кажа, че това е самата идея от която трябва да се тръгне. Иначе има възможности за оптимизация. Нека да дам няколко идеи от които може някой да се възползва.

1. Очевидно е, че трябва да се правят проверки само за нулите, което означава, че за всеки множител от трябва да се търсяи разлагане от вида  Mi = 2^p1 * 5^p2 * Q - тоест всеки множител се представят като произведение на степен на 2-ката и петицата и някакъв остатък! Като степените на 2 и 5 са ясни защо ни трябват, а от остатъка ни трябва само последната цифра.

2. Може да се подходи и итеративно, като се знае числата които се делят на 2 са всички четни(2,4,6,8,10..), а тези на 5 са всички завършващи на 5 или нула (5,10,15,20...) - тук трябва да се смимава за припокриването на тези които завършват на 0 и там може да се допусне грешка. А от всички други ни трябва само последната цифра (с изключение на тези където завършват на 1).

 3. Разбира се, при търсене на последната не нулева цифра, трябва да НЕ се забравя и разликата в степените - например 2^8 и 5^2 =>  остава 2^6, което би трябвало да се прибави към произведението за търсене на тази цифра!

25.10.2010 lexx

Profile

 Здравейте,

Тази задача се свежда до две задачи 

1. Намиране на нулите.

2. Намиране на последната не нулежа цифра;

 

Ще ви кажа каква е идеята за намиране на броя на нулите на които зажършва произведението, защото не гледах тук представените решения и не знам дали са вярни. Но има проста идея която трябва да се приложи, но може да се търсят и оптимизации, защото 50 000 не е малко число! Та каква е идеята ако имаме произведение от числа например 20*12*15 което е 3600, то тук нулите са 2. Но каква е зависимостта, между множителите и броя на нулите. Ами ако се разложи на прости множители всяко от числата

20 = 2*2*5

12 = 2*2*3

15 * 3*5

то се забелязва, че цялото прозиведение може да се представи като 2*2*5*2*2*3*3*5 = 2^4 * 3^2 * 5^2.

За да имаме нули трябва да имаме прозиведение между 2 и 5.

Гелдаме че разложеното произведение на прости множители съдържа 5 на по малка степен, от което следва, че тази степен е нашия търсен резултат за броя на нулите - 5^2.

Тук за този тип задача е полезно да ползвате масив с простите числа до sqrt(50 000) които ще ползвате за разлагането на прости множители;

 

 За втората подзадача ще оставя сами да помислите, както и за самото решение;

13.04.2010 hristov_b

Profile

user: imho

умопомрачителен извод.

     Нещата са естествени, слад като се прочете темата "Указатели и масиви" от учебника на Магдалина Тодорова " Програмиране на С++ - Първа част".

    Елементите и на двумерните масиви се разполагат последователно в паметта  ( по редове) и достъпът до тях може да се осигури по различни начини - по бързия начин е чрез указатели, а по - разбираемия е чрез индекси.

    В повечето компилатори автоматично не се осигурява проверка на стойностите на индексите на масивите - то се прави опционално, с допълнително избиране / неизбиране  на съответната възможност. 

06.04.2010 imho

Profile
Стигнах до един умопомрачителен извод. Нищо не пречи клетките с нули да ги оставям вместо в началото на този ред в края на предния, като с това си спестявам перипетиите по входа. Поне моят компилатор не прави проблеми с индекси от сорта на b[2][-2]. Въпросът е дали с всички компилатори ще е така.

20.03.2010 imho

Profile

Да, явно че така ще е... То моето е същото, ама с iostream.