Angella Andrea - Italian Blog

Infinita passione per lo sviluppo software !
posts - 129, comments - 318, trackbacks - 7

My Links

News

MIT OpenCourseWare: I'm invested Wikipedia Affiliate Button


Sto leggendo:

Archives

Post Categories

Siti web realizzati

Siti web tecnici

Algoritmi – Calcolare l’angolo compreso tra le due lancette dell’orologio


Problema:

Data un’ora, calcolare l’angolo in gradi compreso tra la lancetta dei minuti e la lancetta dei secondi in un orologio analogico (sempre il piu’ piccolo dei due).

Input:

L’input e’ costituito da due interi che rappresentano rispettivamente il valore delle ore (h) e il valore dei minuti (m).

Il valore delle ore e’ un intero compreso tra 0 e 11 estremi compresi.

Il valore dei minuti e’ un intero compreso tra 0 e 59 estremi compresi.

Output:

L’output e’ un numero reale (compreso tra 0 e 180 gradi) che rappresenta l’angolo cercato.

Il mio ragionamento:

Misuriamo gli angoli assoluti in senso orario e partendo dalla positione che indica le 12.

Il valore assoluto dell’angolo della lancetta dei minuti e’ dato da:

m * (360 / 60) = 6 * m

Il valore assoluto dell’angolo della lancetta delle ore e’ dato da:

h * (360 / 12) + (m / 60) (360 / 12) = 30 * h + m / 2

La differenza di questi due angoli in valore assoluto e’ uno dei due angoli tra le lancette:

Abs( 6 * m – (30 * h + m / 2)) =
Abs( 5.5 * m – 30 * h )

Tuttavia se questo angolo e’ maggiore di 180 significa che dobbiamo considerare il suo esplementare:

(360 – angle)

La mia soluzione:

public static double GetAngleBetweenClockHands(byte h, byte m)
{
    if (h < 0 || h > 11) throw new ArgumentOutOfRangeException("h must be between 0 and 11");
    if (m < 0 || m > 59) throw new ArgumentOutOfRangeException("m must be between 0 and 59");

    double angle = Math.Abs(5.5 * m - 30 * h);

    return angle <= 180 ? angle : 360 - angle;
}


Correzioni suggerite:

  • Usare “must be” invece di “has to”
  • Usare l’eccezione piu’ specifica ArgumentOutOfRangeException invece che ArgumentException

Print | posted on martedì 31 agosto 2010 23.30 |

Feedback

Gravatar

# re: Algoritmi – Calcolare l’angolo compreso tra le due lancette dell’orologio

Complimenti! Problema interessante e soluzione che poteva sembrare complessa. In effetti torna per le 3,6,9,12. Mi chiedo: alle 3.30 c'è un angolo di 75 gradi? Non dovrebbe essere di poco meno di 90?
01/09/2010 9.05 | Emanuele
Gravatar

# re: Algoritmi – Calcolare l’angolo compreso tra le due lancette dell’orologio

Si interessante :)

Considera che l'angolo compreso tra due ore consecutive e' pari a 30 gradi quindi la lancetta delle ore in questo caso sara' spostata della meta' e cioe' di 15 gradi. Quindi l'angolo compreso tra le lancette e' di 90 - 15 = 75 gradi !
01/09/2010 10.20 | Andrea Angella
Gravatar

# re: Algoritmi – Calcolare l’angolo compreso tra le due lancette dell’orologio

"Has to be" ;) .... meglio ancora "must be"
01/09/2010 11.52 | Miky
Gravatar

# re: Algoritmi – Calcolare l’angolo compreso tra le due lancette dell’orologio

Ha un problema: ritorna un angolo in [0,180], mentre dovrebbe essere in [0,180[...

Poi penso che il calcolo possa essere semplificato...

Infine, userei ArgumentOutOfRangeException.

-LV
01/09/2010 12.04 | LudovicoVan
Gravatar

# re: Algoritmi – Calcolare l’angolo compreso tra le due lancette dell’orologio

Miky:
grazie per la correzione, il mio inglese e' ancora da perfezionare :)

LV:
Completamente d'accordo per ArgumentOutOfRangeException. Per quanto riguarda l'angolo invece 180 deve essere compreso. Considera le ore 06:00, in quel caso il risultato deve proprio essere 180 gradi. Hai qualche idea di come si possa semplicare il calcolo?
01/09/2010 20.23 | Andrea Angella
Gravatar

# re: Algoritmi – Calcolare l’angolo compreso tra le due lancette dell’orologio

Hai ragione sui 180 gradi, mia svista.

Il calcolo si potrebbe semplificare come segue (copiato e incollato dal mio codice di prova):

---------------------------------------
int h = 3; // in [0, 11]
int m = 5; // in [0, 59]

int hDeg = 30 * h;
int mDeg = 6 * m;

int deltaDeg = hDeg > mDeg ? hDeg - mDeg : mDeg - hDeg;
---------------------------------------

Nota che uso solo tipi int (a parte che non servono i double, int e' meglio di byte per fare calcoli).

Spero che non ci siano bug, non ho fatto test molto estensivi... :)

-LV
01/09/2010 23.30 | LudovicoVan
Gravatar

# re: Algoritmi – Calcolare l’angolo compreso tra le due lancette dell’orologio

Fra l'altro vedo che tu usi 5.5 come fattore per le ore... uno di noi due deve avere un bug!?

Purtroppo adesso devo scappare: magari a dopo...

-LV
01/09/2010 23.32 | LudovicoVan
Gravatar

# re: Algoritmi – Calcolare l’angolo compreso tra le due lancette dell’orologio

> come fattore per le ore

Come fattore per i minuti...

Devo proprio scappare!!

-LV
01/09/2010 23.33 | LudovicoVan
Gravatar

# re: Algoritmi – Calcolare l’angolo compreso tra le due lancette dell’orologio

Ciao Ludovido,
l'errore che tu commetti e' in questa linea di codice:

int hDeg = 30 * h;

Questo codice considera che la lancetta delle ore punti sempre ad una specifica ora (la distanza tra ogni ora in termini di angolo infatti e' di 30 gradi). In realta' non e' cosi' perche' la lancetta delle ore puo' puntare anche in una posizione intermedia e questo dipende dalla posizione della lancetta dei minuti. Per calcolare l'angolo della lancetta delle ore devi anche considerare m, non solamente h.

Come scritto nella sezione "Il mio ragionamento":

hDeg = 30 * h + 30 * (m / 60) = 30 * h + m / 2

Per questo motivo e' necessario utilizzare numeri a virgola mobile.

Ecco un listato degli angoli per tutte le ore con h=3

03:00, 90 degrees
03:01, 84,5 degrees
03:02, 79 degrees
03:03, 73,5 degrees
03:04, 68 degrees
03:05, 62,5 degrees
03:06, 57 degrees
03:07, 51,5 degrees
03:08, 46 degrees
03:09, 40,5 degrees
03:10, 35 degrees
03:11, 29,5 degrees
03:12, 24 degrees
03:13, 18,5 degrees
03:14, 13 degrees
03:15, 7,5 degrees
03:16, 2 degrees
03:17, 3,5 degrees
03:18, 9 degrees
03:19, 14,5 degrees
03:20, 20 degrees
03:21, 25,5 degrees
03:22, 31 degrees
03:23, 36,5 degrees
03:24, 42 degrees
03:25, 47,5 degrees
03:26, 53 degrees
03:27, 58,5 degrees
03:28, 64 degrees
03:29, 69,5 degrees
03:30, 75 degrees
03:31, 80,5 degrees
03:32, 86 degrees
03:33, 91,5 degrees
03:34, 97 degrees
03:35, 102,5 degrees
03:36, 108 degrees
03:37, 113,5 degrees
03:38, 119 degrees
03:39, 124,5 degrees
03:40, 130 degrees
03:41, 135,5 degrees
03:42, 141 degrees
03:43, 146,5 degrees
03:44, 152 degrees
03:45, 157,5 degrees
03:46, 163 degrees
03:47, 168,5 degrees
03:48, 174 degrees
03:49, 179,5 degrees
03:50, 175 degrees
03:51, 169,5 degrees
03:52, 164 degrees
03:53, 158,5 degrees
03:54, 153 degrees
03:55, 147,5 degrees
03:56, 142 degrees
03:57, 136,5 degrees
03:58, 131 degrees
03:59, 125,5 degrees

Curioso il caso:

03:16, 2 degrees

Per quanto riguarda l'utilizzo del tipo byte sai se il compilatore fa qualche sorta di ottimizzazione? Sarei curioso di confrontare l'IL.

Anche semplici algoritmi possono essere divertenti :)
01/09/2010 23.52 | Andrea Angella
Gravatar

# re: Algoritmi – Calcolare l’angolo compreso tra le due lancette dell’orologio

> 'errore che tu commetti e' in questa linea di codice

L'errore e' che la specifica non era esplicita! :) Se consideri angoli frazionari per le ore, allora lo generalizzerei a considerare ore.minuti.secondi.millisecondi... Il tuo 5.5 non mi torna comunque, quando ho tempo provero' a riscrivere e ad eseguire i tuoi casi di test.

> Per quanto riguarda l'utilizzo del tipo byte sai se il compilatore fa qualche sorta di ottimizzazione?

Non so il compilatore C# cosa fa, coccorrerebbe disassemblare, pero' al livello di codice macchina (e quindi in C/C++) int e' piu' veloce perche' e' il tipo nativo: della dimensione di un int sono i registri e tutte gli operandi interi vengono convertiti a int prima di qualunque operazione aritmetica (anche i long su 32 bit sono quindi piu' "lenti").

> Anche semplici algoritmi possono essere divertenti :)

Assolutamente: anche l'implementazione dell'algoritmo piu' semplice non e' mai banale.

-LV
02/09/2010 0.52 | LudovicoVan
Gravatar

# re: Algoritmi – Calcolare l’angolo compreso tra le due lancette dell’orologio

Aggiungere i secondi puo' avere senso in quanto la lancetta dei secondi esiste nei modelli analogici. I millisecondi lascerei perdere.

Come testeresti questo codice?

Fammi sapere :)
02/09/2010 1.01 | Andrea Angella
Gravatar

# re: Algoritmi – Calcolare l’angolo compreso tra le due lancette dell’orologio

Se segui il ragionamento nel post puoi vedere come esce fuori il 5.5 (semplicemente dalla sottrazione dei due angoli)
02/09/2010 1.05 | Andrea Angella
Gravatar

# re: Algoritmi – Calcolare l’angolo compreso tra le due lancette dell’orologio

Usando i TimeSpan si gestiscono anche i millisecondi. Per i test, mi limiterei a testare i vari "boundaries".

Ti allego il codice che ho usato:

--------------------------------------------------------

using System;

namespace TestConsole
{

public class Program
{

public static void Main(string[] args)
{
for (int h = 3, m = 0, s = 0, ms = 0; m < 60; ++m)
{
TestCalcClockHands(h, m, s, ms);
}

Console.Beep();
Console.ReadKey(true);
}

private static void TestCalcClockHands(int h, int m, int s, int ms)
{
TimeSpan clockTime = new TimeSpan(0, h, m, s, ms);

ClockHands clockHands = ClockManager.CalcClockHands(clockTime);

double absHMAngle = Math.Abs(clockHands.MinutesHandAngle - clockHands.HoursHandAngle);

double relHMAngle = absHMAngle > 180d ? (360d - absHMAngle) : absHMAngle;

Console.WriteLine("T = {0} =>\tC = {1}\t: D = {2}", clockTime, clockHands, relHMAngle);
}

}

}

--------------------------------------------------------

using System;

namespace TestConsole
{

public struct ClockHands
{

public double HoursHandAngle;
public double MinutesHandAngle;
public double SecondsHandAngle;

public override string ToString()
{
return string.Format(
"{{ {0}, {1}, {2}, }}"
, HoursHandAngle
, MinutesHandAngle
, SecondsHandAngle
);
}

}

public static class ClockManager
{

public static ClockHands CalcClockHands(TimeSpan time)
{
const double hRat = 360d / 12d;
const double mRat = 360d / 60d;
const double sRat = 360d / 60d;

TimeSpan hTime = time;
TimeSpan mTime = new TimeSpan(0, time.Minutes, time.Seconds);
TimeSpan sTime = new TimeSpan(0, 0, time.Seconds);

double h = hTime.TotalHours % 12d;
double m = mTime.TotalMinutes % 60d;
double s = sTime.TotalSeconds % 60d;

ClockHands clockHands = new ClockHands()
{
HoursHandAngle = h * hRat,
MinutesHandAngle = m * mRat,
SecondsHandAngle = s * sRat,
};

return clockHands;
}

}

}


--------------------------------------------------------

-LV
02/09/2010 22.06 | LudovicoVan
Gravatar

# re: Algoritmi – Calcolare l’angolo compreso tra le due lancette dell’orologio

Pardon, last one... :)

A meno che non si tratti dell'ennesima svista, si semplifica cosi':

-------------------------------------------

public static ClockHands CalcClockHands(TimeSpan time)
{
const double hRat = 360d / 12d;
const double mRat = 360d / 60d;
const double sRat = 360d / 60d;

double h = time.TotalHours % 12d;
double m = time.TotalMinutes % 60d;
double s = time.TotalSeconds % 60d;

ClockHands clockHands = new ClockHands()
{
HoursHandAngle = h * hRat,
MinutesHandAngle = m * mRat,
SecondsHandAngle = s * sRat,
};

return clockHands;
}

-------------------------------------------

-LV
03/09/2010 1.02 | LudovicoVan
Comments have been closed on this topic.

Powered by: