Версия для печати
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум на Исходниках.RU > Pascal > Время и положение стрелок часов


Автор: .exe 11.01.03, 21:56
Please,people!!!


Пусть даны целые числа h,m(часы,минуты),
0<=h<=11, 0<=m<=59, определяющие время суток. Опредилить
наименьшее время(число полных минут), к-е должно пройти
до того момента,когда часовая и минутная стрелки
на цферблате:
а)совпадут;
b)расположатся перпендикулярно друг другу;


                                          ВНИМАНИЕ!!!

!!!МОЖНО ИСПОЛЬЗОВАТЬ ТОЛЬКО ОПЕРАТОР ПРИСВАИВАНИЯ И ВВОДА/ВЫВОДА!!!

Автор: Some1 12.01.03, 08:31
Теория такова:
Представь себе циферблат с двумя стрелками.
У каждой стрелки есть свой угол, в отношении с начальным положением (там где цифра 12).
12    1
|--/  
|  /      2
| /
|/
o

И каждая стрелка с каждым тиком изменяют этот угол по разному:
к примеру минутная стрелка изменяет угол с каждым движением на 360/60 градусов, а часовая на 360/12 градусов.
В то-же время есть взаимоотношение, что каждый тик часовой стрелки, это 60 тиков минутной, тоесть в каждые 360/12 градусов часовой стрелки входит 360 градусов минутной (полный оборот или 60 тиков). Следовательно с каждым движением минутной стрелки на 360/60 градусов часовая стрелка делает движение на (360/12)/60 градусов.
Посчитав всё это можно определить, что часовая стрелка изменяет своё положение на пол градуса в каждый тик минутной стрелки (за одну минуту).

Рассмотрим теперь твою первую задачу:
Пусть у нас даны исходный час - H и исходные минуты - M.
Найдём такое количество минут - DeltaM, при добавлении которых исходному времени углы часовой и минутной стрелки совпали бы. Для этого составим следующее уравнение:
0.5*(H*60+M+DeltaM)=6*(M+DeltaM)
В этом уравнении нам неизвестна только искомая DeltaM. С левой стороны уравнения выражен угол часовой стрелки, посчитанный из общего количества минут умноженных на изменение угла часовой стрелки за каждую минуту. С правой стороны уравнения выражен угол минутной стрелки, без учёта часов, так как минутная стрелка совпадёт с часовой хотябы один раз за час (тоесть меньше чем за час).
Теперь выразим из уравнения искомую DeltaM:
Умножим обе стороны на 2:
H*60+M+DeltaM=12*M+12*DeltaM
Перенесём DeltaM в левую сторону уравнения, а всё остальное в правую:
DeltaM-12*DeltaM=-H*60-M+12*M
-11*DeltaM=-H*60+11*M
Поменяем знаки уравнения:
11*DeltaM=H*60-11*M
Разделим обе части на 11:
DeltaM=(H*60-11*M)/11
DeltaM=H*60/11-M
DeltaM и будет разницей между заданным временем, и временем, когда часовая и минутная стрелки совпадут.

Рассмотрим теперь твою вторую задачу:
В этой задаче у нас углы часовой и минутной стрелки должны отличаться на 90 градусов в любую сторону (минутная левее, или правее).
Следовательно уравнение почти такое же, за исключением того, что надо учесть разницу в градусах в любую сторону:
abs(6*(M+DeltaM)-0.5*(H*60+M+DeltaM))=90
Здесь из угла минутной стрелки вычитается угол часовой, и разница по модулю должна быть равна 90 градусам.
Как и в прошлый раз, умножим обе стороны на 2:
abs(12*M+12*DeltaM-H*60-M-DeltaM)=180
abs(11*DeltaM+11*M-H*60)=180
Далее уравнение раскладывается на два пути:
11*DeltaM+11*M-H*60=180  и  11*DeltaM+11*M-H*60=-180
DeltaM=(180+H*60)/11-M  и  DeltaM=(H*60-180)/11-M
Получится так, что решения должны совпасть, но быть различными в знаках (По моему, но я могу и ошибаться).
Как эту задачу сделать без оператора условия я не знаю :)))

Автор: albom 13.01.03, 21:01
А вот вариант как сделать задачку #2 без оперетора условия.
Вникать, что там Some1 написал не охота, поэтому получаем код, на основе его рассуждений.
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    var result1,result2,result,m,h:longint;
    begin
    readln(h,m);
    result1:=(180+H*60)/11-M; {а может быть и так: result1:=abs((180+H*60)/11-M)};
    result2:=DeltaM=(H*60-180)/11-M; {аналогично}
    result:=result1*byte(result1<result2)+result2*byte(result2<=result1);
    writeln(result);
    end.

Т.е. выражение byte(100<200) равно единице, а выражение byte(5<>5) равно нулю. Это и позволяет найти минимальное значение.

Автор: Some1 14.01.03, 00:56
Не, я так тоже делать умею, тока я не думаю, что у него препод такой зверь :)))
Я как то своему в универе так "оптимизировал прогу" в 10 местах, он меня послал подальше с моими оптимизациями :)))) Пришлось переделывать.
ИМХО - Должен быть другой способ, возможно я где-то ошибся или выбрал не тот метод :)))))

Автор: .exe 14.01.03, 21:56
Ну вы мужики понаписали!
Что за байты у вас в выражениях? Ничего не понимаю!
Объясните поподробнее что это за byte такой и с чем его едят!

Автор: Булат Шакиров 15.01.03, 04:20
Byte -тип такой - целые числа от 0 до 255 - занимает 1 байт. А в данном случае используется приведение типа boolean к этому типу.

Автор: Some1 15.01.03, 09:14
Поясню полнее то, что сказал Ozzy:
Вот смотри, когда ты пишешь
if <чёнить> then <ченить ещё>
то там, где <чёнить> ты всегда пишешь переменную типа boolean - тоесть условие.
Любое условие в паскале рассматривается как переменная типа boolean.
Этот тип может иметь только два значения -True или False.
В первом случае условие - правда, а во втором случае - неправда.
Вот пример:
Если мы напишем (100<500) - то это правда. И следовательно паскаль воспримет всё это выражение как переменную типа boolean, равную true.
Если мы теперь напишем if (100<500)=true then <Чёнить>, то наше условие выполнится, и выполнится <чёнить>
А если написать if (100<500)=false then <Чёнить>, то мы получим расхождение, и условие не выполнится, и не выполнится наше <чёнить>.
Теперь смотри дальше - внутренне в паскале тип Boolean занимает один байт в памяти, и является по сути байтом, у которого 0 - это неправда (False в паскале) и 1 - это правда (True в паскале).
В паскале есть такое хорошее свойство - приведение типов, при помощи него можно узнать, чему равен байт переменной типа Boolean, если взять эту переменную в скобки и перед ней написать byte.
byte(1<10) будет равен 1 так как 1 действительно меньше 10 и значит наш Boolean=True и в его байте содержится 1.
byte(1>10) будет равен 0 так как 1>10 это неправда и значит наш Boolean=Falseи в его байте содержится 0.
Если написать write(byte(100<500)) на экран выведется "1".
Всё это удобно для использования условий прямо в операторах присваивания.
Ведь если умножать на "0" то получится "0", а если умножать на "1" то получится то число, которое мы на "1" умножали :)))
Смотри, тебе написали: result:=result1*byte(result1<result2)+result2*byte(result2<=result1);
Если result1<result2 это правда, то первый byte будет равен 1, а второй 0, и тогда result1 будет умножен на 1, а result2 на 0, в результате чего он сократится, и result будет равен result1.
Если же result1<result2 это неправда, то первый byte будет равен 0, а второй 1, и тогда result1 будет умножен на 0 и сократится, а result2 на 1 и result будет равен result2.
Таким образом вся эта запись является аналогом такой:
If (result1<result2)=true then result:=result1 else result:=result2;

Автор: .exe 15.01.03, 20:50
Some1, большое тебе спасибо!
Теперь всё хорошо ясно!

Автор: .exe 29.01.03, 04:08
Люди! Нашёл в задаче а ужасный баг( b пока не смотрел)!
В результате проги мы находим просто кол-во минут до времени ,
в к-е стрелки совпадут. Т.е. ответ в некоторых случаях получается отрицательным!
А нам надо найти кол-во минут до ближайшего момента когда стрелки совпадут, отсчитывая этот момент с заданного времени!
Люди у кого какие идеи? Может другой алгоритм предложите (хотя и этот хорош -
только где то, что-то нужно подправить)?

Автор: 7in 29.01.03, 15:44
А почему ты в "Алгоритмы" не напишешь?

Текущее положение стрелок (будем считать по большим цифрам, т.е. значения = 0..12):
h = h mod 12
Hpos = h + m/60 - позиция часовой стрелки
Mpos = m/5 - позиция минутной стрелки

Теперь... если Mpos < Hpos, значит минутная стрелка находится раньше часовой (относительно 12 часов), и они совпадут в этом часе. Если же Mpos > Hpos, значит позже, и они совпадут в следующем часе.
[*] В первом случае:
h + m/60 = m/5
h = m*11/60

Msovpadut = h*60/11
Minterval = Msovpadut - m

[*] Во втором случае:
h = (h + 1) mod 12
h + m/60 = m/5
h = m*11/60

Msovpadut = h*60/11
Minterval = 60 + Msovpadut - m


Это теория... попробуй...
(то, что синим прогать не надо - это вывод формулы :))

Автор: 7in 29.01.03, 16:18
Во! Вроде работает! ;D

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    Var
       h, m: Integer;
       Hpos, Mpos, Msovp, Mint: Real;
     
    Begin
       Write('Введите Часы Минуты: ');
       ReadLn(h, m);
       h := h mod 12;
       Hpos := h + m/60;
       Mpos := m/5;
       If Mpos < Hpos then
       Begin
          Msovp := h*60/11;
          Mint := Msovp - m
       End
       else If Mpos > Hpos then
       Begin
          h := (h + 1) mod 12;
          Msovp := h*60/11;
          Mint := 60 + Msovp - m
       End;
       WriteLn('Через ', Trunc(Mint), ' минут ', Trunc(Frac(Mint)*60), ' секунд')
    End.

Автор: Some1 29.01.03, 19:45
To Jin-X: Жека, прочитай внимательно последнюю строку условия (первого поста) :)))
To .exe: Ты часом условие не подправлял ? :)) А то мне кажется, там писалось, что надо найти время (без "которое должно пройти") до совпадения стрелок, кажется автоматически подумал, что не важно, в какую сторону время считать :)))
Короче тогда делай, как сказал Jin-X :))
З.Ы. Жек, у тебя точно правильно ? А какой наименьший шаг у твоей часовой стрелки?? Не размером ли с пять минут ?... я что-то сегодня не очень соображаю :))) (ну в смысле у тебя не берётся за правило, что часовая стрелка делает одно движение в пять минут, и исходя из этого высчитывается угол ?) Я могу ошибаться :)

Автор: 7in 30.01.03, 20:19
Нет, не в 5 минут... Если она на 13 минутах, то будет 2.6 :)
Ну если учесть последнюю строку условия, тогда так:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    Var
       h, m: Integer;
       Hpos, Mpos, Msovp, Mint: Real;
       After: Boolean;
     
    Begin
       Write('Введите Часы Минуты: ');
       ReadLn(h, m);
       h := h mod 12;
       Hpos := h + m/60;
       Mpos := m/5;
       After := Mpos > Hpos;
       h := (h + Ord(After)) mod 12;
       Msovp := h*60/11;
       Mint := 60*Ord(After) + Msovp - m;
       WriteLn('Через ', Trunc(Mint), ' минут ', Trunc(Frac(Mint)*60), ' секунд')
    End.

P.S. Что, собственно, однохренственно ;D

Автор: .exe 01.02.03, 20:50
Мужики, я уж извиняюсь, но  как эту задачу можно сделать, если мало того, что оператор условия не дают использовать, так ещё и булевские переменные не разрешают?

Автор: Some1 01.02.03, 21:55
Ужас.......
А промежуточные булевские можно ?
Ну без объявления самой переменной, ну как у меня выше..
я не объявлял булевскую, а только в присваиваниях писал булевское условие. Это тоже нельзя ?

Автор: 7in 02.02.03, 14:10
Ну хорошо!!!

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    Var
       h, m: Integer;
       Hpos, Mpos, Msovp, Mint: Real;
     
    Begin
       Write('Введите Часы Минуты: ');
       ReadLn(h, m);
       h := h mod 12;
       Hpos := h + m/60;
       Mpos := m/5;
       h := (h + Ord(Mpos > Hpos)) mod 12;
       Msovp := h*60/11;
       Mint := 60*Ord(Mpos > Hpos) + Msovp - m;
       WriteLn('Через ', Trunc(Mint), ' минут ', Trunc(Frac(Mint)*60), ' секунд')
    End.

Булевских переменных нет!
(есть только булевские выражения, но это же не переменные) ;D

Автор: .exe 02.02.03, 20:53
Сформулировал не полностью - булевские выражения тоже низззя !!!! ???

Автор: Serega_f1 03.02.03, 08:25
Наверное, что-то вроде этого:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    program clock1;
    var
     h,m,s,t:integer;
    begin
     readln(h,m);
     s:=((m-5*h-(m div 12)) div abs(m-5*h-(m div 12))+1) div 2;  (* s=0, если минутная стрелка
     позади часовой, иначе s=1 *)
     t:=(60-m)*s;
     h:=h+s;
     m:=m*(1-s);
     t:=t+5*h-m+(h-1) div 2;
     writeln(t);
    end.

Автор: Serega_f1 03.02.03, 09:58
А лучше - вроде этого:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    program clock2;
    var
     h,m,s,t:integer;
    begin
     readln(h,m);
     t:=5*h-m+(h-1) div 2;
     s:=((t div abs(t))+1) div 2;
     t:=t+65*(1-s);
     writeln(t);
    end.

Автор: .exe 15.02.03, 22:40
Кто-нибудь, пож-ста, поясните последнее решение!

Автор: DOCTOR-121 16.02.03, 04:03
to exe смотри вроде так
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    programm cl;
    var
    z,j,h,m:integer;
    begin
    readln(h,m);
    j:=h*5 div m;
    if j=0 then
    writeln('Svpodaet');
    if m:=h*5+30 then
    writeln('perpendikylarni!');
    end.

Автор: DOCTOR-121 16.02.03, 04:06
извини я условие мельком прочитал!!!

Powered by Invision Power Board (https://www.invisionboard.com)
© Invision Power Services (https://www.invisionpower.com)