Kirjautuminen

Haku

Tehtävät

Keskustelu: Koodit: PHP: Neliöjuurilaskin

Sivun loppuun

kayttaja-2791 [15.04.2005 16:36:51]

#

Laskee neliöjuuren.
Valitettavasti en osaa kovinkaan tarkasti selittää iterointimenelmää kun tein tämän koodin 6kk sitten enkä sitä silloin kommentoinut.
Toivottavasti minua ei nyt sitten lynkata kun tälläisiä tyhjänpäivisiä vaikeaselkoisia koodeja tänne lähettelen :|
Jokatapauksessa, mitään hienoa sarjakehitelmää ei ole käytetty vaan homma on "keksitty" ihan itse.
Minkä takia varmaankin tämä häviää nopeudessa PHP:n omalle sqrt() funktiolle 100-0.
Mutta toisaalta ehkä tällä voi saada tarkempia tuloksia...
Ainakin omien testien mukaan tämä palauttaa ainakin osassa tapauksissa tarkemman likiarvon kuin PHP:n oma kunhan tarkkuuskerroin on tarpeeksi suuri.

Tämä on täysi html-versio, mutta helposti tuosta tuon pelkän funktionkin nappaa jos joku todella sitä tarvitsee.

Neliojuuri.php

<?php
/*
Laskee neliöjuuren.
Valitettavasti en osaa kovinkaan tarkasti selittää iterointimenelmää kun tein tämän koodin 6kk sitten enkä sitä silloin kommentoinut.
Toivottavasti minua ei nyt sitten lynkata kun tälläisiä tyhjänpäivisiä vaikeaselkoisia koodeja tänne lähettelen :|
Jokatapauksessa, mitään hienoa sarjakehitelmää ei ole käytetty vaan homma on "keksitty" ihan itse.
Minkä takia varmaankin tämä häviää nopeudessa PHP:n omalle sqrt() funktiolle 100-0.
Mutta toisaalta ehkä tällä voi saada tarkempia tuloksia...
Ainakin omien testien mukaan tämä palauttaa ainakin osassa tapauksissa tarkemman likiarvon kuin PHP:n oma kunhan tarkkuuskerroin on tarpeeksi suuri.

Tämä on täysi html-versio, mutta helposti tuosta tuon pelkän funktionkin nappaa jos joku todella sitä tarvitsee.
*/
?>
<html>
<head>
<style>
body {
  font: 90% verdana, sans-serif;
}

input {
  background: #e5f5ff;
  border: 1px solid black;
  margin: 3px;
  padding-left: 3px;
  text-align: center;
}

</style>
</head>
<body>
<h3>Neliöjuurilaskuri</h3>
<?php
print "<form><input name='luku' type=text value='".$_GET['luku']."' size=4><input type=submit value=Laske></form>";
function neliöjuuri($luku, $tarkkuuskerroin = 1, $tekstimuoto = 0) {
	$luku = str_replace(",", ".", $luku);
	if (!is_numeric($luku))
		$error .= "Syötettä ei tunnistettu luvuksi!<br>";
	if ($luku < 0)
		$error .= "Negatiivisesta luvusta ei voi ottaa neliöjuurta!";
	//Tarkkuuskerroin, helppo raiskata tälläisenään. 1 pitäisi tuottaa tyydyttävää 4 desimaalin tarkkuutta ainakin pienempien kuin 10 000 lukujen osalta
	if ($tarkkuuskerroin <= 0)
		$tarkkuuskerroin = 1;
	//Muuttuja B on eräänlainen aloituspiste josta arvoa lähetään iteroimaan. Jos 2*$b on alkuperäinen luku iterointi ei toimi vaan tyssää alkuunsa. Sen takia tämä.
	$b = 2;
	while (2*$b == $luku)
		$b = 1.5*$b;

	//Sitten aletaan iteroimaan mikäli virheitä ei ole esiintynyt
	if (!$error) {
		/*
		Miten tämä iterointi menee?

		Hyvä kysymys... Tein tämän about 6kk sitten enkä tietenkään tätä silloin kommentoinut. Eli tehdäänpä pientä reverse engineerausta.

		$b = 1. $a on puolet annetusta luvusta.
		Mikäli $a*$a on suurempi kuin annettu luku, pienennetään sitä puoleen $a:n ja $b:n erotuksen itseisarvosta.
		Missä siis $b on alkuarvauksen jälkeen edellisen iterointikierroksen tulos...
		Tai jotain vastaavaa :D

		Jos joku osaa syvemmin tuota selittää niin hyvä, Ohjelmointiputkan kommentteihin vain.
		*/
		for ($i = 0, $a = $luku / 2; $i < (50 * $tarkkuuskerroin); $i++) {
			if ($a*$a > $luku)  {
				$c = $a;
				if ($b > $a)
					$a = $a - (($b - $a) / 2);
				else
					$a = $a - (($a - $b) / 2);
				$b = $c;
			}
			else  {
				$c = $a;
				if ($b > $a)
					$a = $a + (($b - $a) / 2);
				else
					$a = $a + (($a - $b) / 2);
				$b = $c;
			}
		}
		//Mikäli käyttäjä haluaa tekstimuodossa laitetaan tekstit
		if ($tekstimuoto) {
			if (round($a, 1)*round($a, 1) == $luku)
				$arvio = "tarkalleen";
			else
				$arvio = "likimain";
			$a = round($a, (4*$tarkkuuskerroin));
			return "Neliöjuuri luvusta <b>$luku</b> on $arvio <b>$a</b>";
		}
		//Muuten palautetaan pelkkä luku
		else
			return $a;
	}
	else
		return $error;
}
print neliöjuuri($_GET['luku'], 1, 1);
?>

Antti Laaksonen [17.04.2005 00:49:41]

#

Tässä on toinen tapa laskea neliöjuuria:

<?php

// luku, jonka juuri lasketaan
$luku = 123;
// aloitusarvaus
$tulos = 10;

for ($i = 0; $i < 10; $i++) {
    // neliöjuuri
    $tulos = ($luku / $tulos + $tulos) / 2;
    // kuutiojuuri
    // $tulos = ($luku / $tulos / $tulos + 2 * $tulos) / 3;
    echo "$tulos\n";
}

?>

Uusi tulos on vanhan tuloksen ja luvun ja vanhan tuloksen osamäärän summan keskiarvo. Näin oikea vastaus alkaa tarkentua yllättävän nopeasti. Jos aloitusarvaus on kaukana lopputuloksesta, kierroksia tarvitaan enemmän. Samanlaisia kaavoja voi keksiä myös muille juurille. Kommenteissa näkyy kuutiojuuren laskeva kaava, jonka periaate on sama.

kayttaja-2791 [17.04.2005 02:02:24]

#

Vasiten ajoin jotain testejä, tuo Antin versio antaa paremman tuloksen vaikka iteraatiokierroksia on vain alle viides... Eli se siitä tehokkuudestakin :)

No kuitenkin, innostuin esimerkistäsi hommaan uudestaan ja teinkin homman vielä Newtonin menetelmällä:

<?php

//Luku, jonka neliöjuuri lasketaan
$luku = 500;
//Alkuarvaus, muuten ei paljoa väliä kunhan ei ole nolla ettei nimittäjä mene nollaksi
$tulos = 1;

/*
Neliöjuuren lasku Newtonin menetelmällä
	Merkitään
		x2 = x(i+1)
		x1 = x(i)

	Siis toisinsanoen x1 on edellisen kierroksen saama arvo ja ekalla kierroksella se on tuo alkuarvaus.

	Newtonin menetelmä menee seuraavasti:
		x2 = x1 - f(x1) / f´(x1)

	Missä
		f(x) = x * x = $luku
	<=> f(x) = x * x - $luku

	Ja derivaatta on tietenkin
		f´(x) = 2x

	Eli saadaan seuraavaan muotoon:
		x2 = x1 - ((x1 * x1 - $luku) / (2 * x1))

	Ja PHP:n versio siitä:
		$tulos = $tulos - (($tulos * $tulos - $luku) / (2 * $tulos));
*/

for ($i = 0; $i < 15; $i++) {
	$tulos = $tulos - (($tulos * $tulos - $luku) / (2 * $tulos));
	echo "$tulos\n<br />";
}
?>

Muistelisin että laskimetkin saattavat käyttää tätä tapaa, tai sitten Simpsonia. En tarkalleen enään muista kun näistä kursseista on jo sen verran aikaa.

Edit:
No piru, ympäri mennään ja yhteen tullaan :D

Eli Antin versio on johdettu juurikin tuosta Newtonin menetelmästä:

Omani oli siis:
	     x - (x*x - y) / 2x
			Missä y = $luku = luku josta neliöjuurta haetaan

	<=> (2*x*x - x*x + y) / 2x
			Supistetaan x:llä
	<=> (2*x - x + y/x) / 2
			Sievennetään
	<=> (x + y/x) / 2
			On yhtäkuin Antin versio

Jos tämä nyt edes ketään kiinnostaa :D Mutta olipahan ainakin jotain tekemistä taas yön tunneilla.

tejeez [17.04.2005 08:52:17]

#

Tässä myös jokin tapa laskea neliöjuuri. Toimii kyllä vain kokonaisluvuilla.

<?php
$luku = 100;
$tulos = 0;
while($luku > 0) {
  $tulos++;
  $luku-=2*$tulos;
}
echo $tulos;
?>

Antti Laaksonen [17.04.2005 10:18:27]

#

Tässä on toinen tapa laskea neliöjuuri, jos tuloksena tiedetään olevan kokonaisluku.

<?php

$luku = 361;
$alku = 0;
$loppu = $luku;

while(1) {
    $keski = ceil(($alku + $loppu) / 2);
    $neliö = $keski * $keski;
    if ($neliö > $luku) {
        $loppu = $keski;
    } elseif ($neliö < $luku) {
        $alku = $keski;
    } else {
        echo $keski;
        break;
    }
}

?>

Ohjelman toiminta muistuttaa binäärihakua. Joka kierroksella lasketaan alku- ja loppukohdan keskiarvo ja lasketaan sen neliö. Jos tulos on alkuperäistä lukua suurempi, siitä tulee uusi yläraja. Jos taas tulos on pienempi, siitä tulee alaraja. Kun tulos on sama kuin luku, vastaus on löytynyt. Luvun 361 neliöjuuri (19) löytyy 6 kierroksen jälkeen, ja luvun 52345225 neliöjuuri (7235) löytyy 25 kierroksen jälkeen. Tässä ei tarvita ollenkaan alkuarvausta.

teemu_puurunen [17.04.2005 14:36:45]

#

Tässä on vielä pieni koodi, jolla voi neliöjuuren lisäksi laskea vaikka kuutiojuuren, tai yleensäkään n-juuren.

<?php

// Luku ja juuri
$luku = 361;
$n = 2;

// Lasketaan neliöjuuri
$njuuri = pow($luku, 1/$n);
echo "<p>$n-juuri luvusta $luku on $njuuri</p>";

// Lasketaan esimerkiksi vielä kuutiojuuri
$n = 3;

$njuuri = pow($luku, 1/$n);
echo "<p>$n-juuri luvusta $luku on $njuuri</p>";

?>

tuoppi [17.04.2005 19:03:37]

#

Hmm.. Tuo (alkuperäinen versio) sanoo että "Neliöjuuri luvusta 1.999 on likimain 1"? Ja tuo sama tulee kaikilla muotoa 1 pilkku jotain olevilla luvuilla.

kayttaja-2791 [17.04.2005 19:34:26]

#

lainaus:

Hmm.. Tuo (alkuperäinen versio) sanoo että "Neliöjuuri luvusta 1.999 on likimain 1"? Ja tuo sama tulee kaikilla muotoa 1 pilkku jotain olevilla luvuilla.

Bugi, korjattu. Johtui alkuarvauksesta 1. Vaihdoin sen 2:teen jolloin tuota/muitakaan ongelmia ei pitäisi olla.

Deewiant [17.04.2005 20:45:06]

#

Pyöristysvikaa: "Neliöjuuri luvusta 591 004 237 824 on tarkalleen 768 767.9999" - pitäisi olla 768768.

Muutenkin näyttää hajoilevan isoilla luvuilla, en sitten tiedä onko vika metodissa vai koodissa. "Neliöjuuri luvusta 349 286 009 125 927 152 254 976 on likimain 591 140 017 282.62" - pitäisi olla 591 004 237 824.

kayttaja-2791 [17.04.2005 20:53:27]

#

lainaus:

Pyöristysvikaa: "Neliöjuuri luvusta 591 004 237 824 on tarkalleen 768 767.9999" - pitäisi olla 768768.

Muutenkin näyttää hajoilevan isoilla luvuilla, en sitten tiedä onko vika metodissa vai koodissa. "Neliöjuuri luvusta 349 286 009 125 927 152 254 976 on likimain 591 140 017 282.62" - pitäisi olla 591 004 237 824.

Jooh, vika on tässä tapauksessa funktion käytössä ei siinä itsessään:

<?php
//Tarkkuuskerroin, helppo raiskata tälläisenään. 1 pitäisi tuottaa tyydyttävää 4 desimaalin tarkkuutta ainakin pienempien kuin 10 000 lukujen osalta

//Ja lopussa
print neliöjuuri($_GET['luku'], 1, 1);

Eli jos joku haluaa tehdä laskimen joka laskee teramiljardit oikein niin muuttakoot, itse en halua alkaa kyykkäämään hosting-tarjoajaani.

Edit:
Pyöristyksistä en sano tässä vaiheessa mitään... Liekkö edes korjaan, ei liene niin oleellista kun koko koodivinkin tarkoitus on vain esitellä vaihtoehtoisia tapoja laskea neliöjuuri...

T.M. [17.04.2005 21:02:43]

#

No ei tuolla saa edes tarkempia arvoja:
"Neliöjuuri luvusta 124 on likimain 11.1355" :E

Jaska [17.04.2005 23:48:15]

#

Tästä keskustelusta tulikin mieleen: Tietääkö kukaan mitään kirjaa missä olisi käsitelty matemaattisia algoritmejä. googlehan on tietysti olemassa, mutta olisi kiva tietää jokin kirja missä olisi kaikennäköisiä matemaattisia algoritmeja. Cormenin at alin Introduction to algorithms minulla on jo, mutta kaipaisin enemmän matematiikkaan keskittyviä algoritmejä, esim. numeerista laskentaa käsitteleviä opuksia.

jideko [21.04.2005 00:57:35]

#

lainaus:

if ($luku < 0)
$error .= "Negatiivisesta luvusta ei voi ottaa neliöjuurta!";

Kaikista luvuista voi otaa neliojuuren! Vastaus ei vain ole valttamatta reaaliluku.
nj(-1)=i
nj(-4) voidaan merkita nj(4)*nj(-1), joka on 2i.

Jaska [22.04.2005 17:37:43]

#

On totta että jokaisella kunnalla on tosiaan algebrallinen sulkeuma. Reaalilukujen tapauksessa tuo sulkeuma on kompleksiluvut. Täällä juurilla ei ole kuitenkaan yksikäsitteistä ratkaisua, vaan juuria on tarkasteltava haarojen avulla.

Macro [18.05.2010 16:17:23]

#

Tai kenties sqrt-funktio olisi hauska.


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta