Voorkom exponentiële notatie in PHP

Status
Niet open voor verdere reacties.

ErikBooy007

Terugkerende gebruiker
Lid geworden
24 mei 2007
Berichten
3.814
Ik ben gisteren begonnen met Project Euler (voor de wiskunde / informaticaliefhebbers) en nu zit ik met een probleempje.

De vraag die gesteld wordt is:

n! means n × (n − 1) × ... × 3 × 2 × 1

Find the sum of the digits in the number 100!

Ik zou het trouwens wel waarderen, als jullie hier niet even het antwoord posten ;)


Nu is het punt, ik kan 100! (honderd-faculteit) wel uitrekenen, maar het wordt als exponentieel getal weergegeven (9.3....E157 geloof ik). Nu kan ik dus de waarden van de digits niet optellen.

Nu is mijn vraag, kun je PHP zo instellen, dat deze niet de exponentiele notatie gebruikt?

Ik heb al in PHP.ini de precision naar 200 gezet, maar dat helpt ook niet. Daarnaast heb ik bcmath.scale op 200 gezet en de bcmul functie gebruikt en dat mocht ook niet baten.
 
Nu geeft ie het wel goed weer :shocked: .

Nou ja, nutteloos topic dus (mijn excuses mods, niet de bedoeling :o)
 
Meestal als je de php.ini aanpast moet je ook de webserver herstarten, misschien was dat het :P

Kan hij 100! aan trouwens als getal? Ik dacht dat ie daarvoor al stuk ging.

Trouwens, ik heb ook ooit een kleine math library gemaakt om Combinatoriek te doen, misschien dat je daar wat aan hebt? :P
 
Ja, ik had hem wel herstart, maar m'n WAMP-logootje bleef geel (nog niet helemaal klaar met opstarten geloof ik ;)). Dus ik ben gewoon verder gegaan, want m'n pagina's werden wel geparsed.

100! kon ie inderdaad aan ja, maar nu moet ik met getallen van 1000 cijfers gaan rekenen en hoe ik dat ga doen weet ik ook nog niet. PHP geeft nu steeds bij 10^308 zo'n beetje INF aan. Ze vragen namelijk wat het eerste nummer in de Fibonacci reeks is met meer dan 1000 tekens... :shocked:

Heb je daar misschien nog ideeën voor? :P Waarschijnlijk gaat het er op neer komen dat ik weer een functie moet schrijven om strings bij elkaar op te tellen... ;)

En als ik die math-lib van je zou mogen hebben, zou dat wel heel mooi zijn! :thumb:
 
Laatst bewerkt:
http://ruigekonijnen.nl/temp/mathhelper.rar

Dan kun je iig combinatoriek van het niveau 2500 ncr 125 doen :) (Of andersom, ik vergeet de notatie altijd)

Je kunt voor dit soort dingen een eigen Class bouwen die met oneindig grote Int kan rekenen? Dat is het makkelijkste denk ik...
 
Dank je!!

Dat is mooi want combinatoriek heb ik voor een van de volgende opgaven ook nodig (daar zit trouwens geen native functie voor in PHP of wel?). ;)

En kijk, dat laatste punt dat gaat m'n petje te boven. Ik kan wel een functie ( / class ftm ) schrijven die strings gebruikt in plaats van integers en die optelt, maar hoe kan ik een oneindig grote integer maken als PHP bij 10^308 al infinite geeft?
 
Wat ik zou doen is een Class maken die een 10.000 tallig getallen stelsel implementeert :)

Dan moet je alle rekenkundige operaties opnieuw defineren; je houdt in je Class een lijst bij met getallen tussen 0 en 9999 in een array, en daarop doe je de berekeningen.

Dus als je bijv. het getal 100.001 aan je class geeft ( GrootGetal->setValue( 100001 ) ) dan maakt hij daar intern een array van, op deze manier: ( $aGetallen[ 10, 1 ] )

Dan is elk getal in de array 4 posities van je eindgetal, dus [10][0001] bij de output.

Als je dan wat rekenfuncties toevoegd die bijv. als je huidige getal 9999 is, en je er 1 bij optelt, het getal door laat shiften naar de volgende array en huidige reset, dan zou je een heel eind moeten komen... als je het nog volgt :P
 
Ik geloof dat ik het ongeveer snap! Is wel weer een slimme oplossing, maarja dat zijn we wel van je gewend ;).

Alleen, hoe gebruik je dan GrootGetal->setValue( 100001 ) , maar dan met een hele grote waarde. Dan moet je toch alsnog eerst die waarde aan het script voeren ? Of zie ik dat nu verkeerd?
 
Dan zou je een statische functie toe moeten voegen die een numeric string ontvangt, en die hem uitleest, omzet naar een GrootGetal en dat object teruggeeft :)
 
Ja, oké, dat kan natuurlijk!

Je wordt in ieder geval weer hartelijk bedankt! Ik ga hier eens mee stoeien als ik terug ben van m'n werk ;)
 
Ik ben hier net weer even mee bezig geweest en heb nu de functie voor het optellen van extreme getallen af :)

Nu heb ik nog niet echt vaker met OOP gewerkt (i know, schaam schaam), maar dit is wat ik ervan gebakken heb:

PHP:
<?php

class extremeMath {

	var $value;
	
	function extremeMath( $input ) {
		$this->value = $input;
	}
	
	function getValue(){
		return $this->value;
	}
	
	function multiply ( $sFactor ) {
		
	}
	
	function divide ( $sFactor ) {
		
	}
	
	function substract ( $sFactor ) {
		
	}
	
	function add ( $sFactor ) {
		if ( strlen ( $sFactor ) > strlen ( $this->value ) ) {
			$iC = strlen ( $sFactor );
			$aFactor = array_reverse ( str_split ( $sFactor ) );
			$aValue = array_reverse ( str_split ( $this->value ) );
		} else {
			$iC = strlen ( $this->value );
			$aFactor = array_reverse ( str_split ( $sFactor ) );
			$aValue = array_reverse ( str_split ( $this->value ) );
		}
		
		$iRest = 0;
		$aReturn = array();
		
		for ( $i = 0; $i <= $iC - 1; $i++ ) {
			
			$sValue = $this->value;
			
			$iReturn = intval ( $aValue[$i] ) + intval ( $aFactor[$i] );
			
			$aReturn[$i] = $iReturn % 10 + $iRest;
			
			if ( $iReturn > 9 ) {
				
				$iRest = 1;
				
			} else {
				
				$iRest = 0;
				
			}
			
		}
		
		$aReturn = array_reverse ( $aReturn );
		
		return ( implode ( '', $aReturn ) );
		
	}	

}
?>

Nu heb ik het idee, dat dit makkelijker móet kunnen, aangezien ik een aantal keren arrays omdraai, maar toch leek dit de enige manier te zijn om het geheel te laten werken. (Elke array_reverse was wel doordacht ) ;)

Maar nu vraag ik mij meteen af, of ik het concept OOP zo wel op de juiste manier gebruik, of zijn er nog wel aandachtspuntjes?

Overigens met deze functie net twee getallen van 35.000 digits probleemloos bij elkaar opgeteld in 0,27s, op een vreselijk langzame PC ;)

Nog even voor de benchmark ;) : twee getallen van 1.000.000 digits : 8.64s
 
Laatst bewerkt:
Persoonlijk zou ik het iets anders gedaan hebben...

Ik ben er zelf ook even mee bezig geweest, en heb het nu zo aangepakt:

PHP:
	$oInt1 = BigInt::createBigIntFromString( $sBigNum1 );
	$oInt2 = BigInt::createBigIntFromString( $sBigNum2 );
	$oInt3 = BigInt::add( $oInt1, $oInt2 );
	$oInt3->show();

Dan behoud je namelijk de input values, want in jouw implementatie ben je die kwijt... als je dus A + B wilt berekenen en daarna ook nog A * B kom je in de problemen, want je A en B bestaan al niet meer :)

Op dit moment kan ik twee getallen van 1 miljoen digits optellen in ongeveer 1 seconde, maar hij doet nog geen negatieve getallen atm.
(Overigens heb ik de tijd voor het genereren van die getallen er wel af gehaald, dat duurt ruim 2 seconden namelijk :P)

Hij gebruikt ongeveer 35 MB geheugen, de arrays van PHP zijn minder efficient dan ik dacht/hoopte.

Waarschijnlijk kan ik er nog aardig wat af krijgen, maar goed het is niet bedoeld voor de echt (als je dat soort getallen op wil tellen moet je toch eens naar een andere taal gaan kijken xD)

Dit is de code die ik nu heb:

PHP:
/**
 * Adding up very large, positive numbers.
 * Class is work in progress ;)
 * 
 * Author: Erik Roelofs
 */
class BigInt {

	// size of the chunks the string/number is broken up into (ie: into sets of 8 digits)
	private static $iChunkSize 		= 8;
	// filled automatically; pow ( 10, chunksize )
	private static $iChunkMaxAmount = 0; 
	
	/**
	 * Each of these values is four digits in the bug number.
	 * The order of values is in REVERSE! 
	 */
	private $aValues = array();
	
	/**
	 * Constructing a new big int
	 * Determine the max chunk value, if we do not have it yet
	 *
	 */
	public function __construct() {
		if ( self::$iChunkMaxAmount == 0 ) {
			 self::$iChunkMaxAmount = pow( 10, self::$iChunkSize );
		}
	}
	
	/**
	 * Create a BigInt from a string
	 *
	 * @param string $sString
	 * @return BigInt $oBigInt
	 */
	public static function createBigIntFromString( $sString ) {
		// make sure it is of proper type
		if ( !ctype_digit( $sString ) ) {
			 throw new Exception( 'Given string is not composed of digits.' );
		}
		// find the nearest multiple of the chunksize, to split the string properly
		// otherwise splittig 100 in chunks of 2 yields 10, 0 instead of 1, 00.
		$iLength = strlen( $sString );
		if ( ( $iLength % self::$iChunkSize ) != 0 ) {
			$iLength = ceil ( $iLength / self::$iChunkSize ) * self::$iChunkSize;
			$sString= str_pad( $sString, $iLength, '0', STR_PAD_LEFT );
		}

		$aDigits = str_split ( $sString , self::$iChunkSize );
		// make BigInt, return it.
		$oBigInt = new self;
		$oBigInt->setSets( array_reverse( $aDigits ) );
		return $oBigInt;
	}
	
	/**
	 * Return a new BigInt, with the value of the two given BigInts added together 
	 *
	 * @param BigInt $oBigInt1
	 * @param BigInt $oBigInt2
	 * @return BigInt $oResult
	 */
	public static function add( BigInt $oBigInt1, BigInt $oBigInt2 ) {
		
		// store the result here
		$oResult = new BigInt;
		
		// determine which of the two is the longest; we'll run over that one
		if ( $oBigInt1->getLength() > $oBigInt2->getLength() ) {
			$oRunner 	= $oBigInt1;
			$oFollower 	= $oBigInt2;
		}
		else {
			$oRunner 	= $oBigInt2;
			$oFollower 	= $oBigInt1;
		}
		
		// carryover from the last operation
		$iCarry = 0;
		
		// run over the longest, and add each set to the set of the shorter
		foreach ( $oRunner->getSets() as $iSetNum => $iValue ) {
			// get the value of the follower on this position
			$iOtherValue = $oFollower->getSet( $iSetNum );
			// determine the total for the current set
			$iTotal = $iValue + $iOtherValue + $iCarry;
			
			// if the total is more then the max chunk size, then the number will overflow and we'll have to determine the new carry
			if ( $iTotal >= self::$iChunkMaxAmount ) {
				$iCarry = ( floor ( $iTotal / ( self::$iChunkMaxAmount ) ) );
				$iTotal = $iTotal % ( self::$iChunkMaxAmount );
			}
			else { 
				$iCarry = 0;
			}
			// add this chunk to the resultset
			$oResult->addSet( $iTotal );
		}
		// now, if all the numbers are done and we still have a carry, tack that to the front of the result
		if ( $iCarry > 0 ) {
			$oResult->addSet( $iCarry );
		}
		// return the result
		return $oResult;
	}
	
	/**
	 * Get the set of numbers at position $iSet
	 * For example; if the BigInt is 125042, then 0 would be 5042, and 1 would be 12 
	 * Will return 0 if the number isn't that long.
	 * 
	 * @param integer $iSet
	 * @return integer
	 */
	private function getSet( $iSet ) {
		if ( isset ( $this->aValues[ $iSet ] ) ) { 
			return $this->aValues[ $iSet ];
		}
		return 0;
	}
	
	/**
	 * Return all the sets in this number
	 * 
	 * return array $aSets
	 */
	private function getSets() { 
		return $this->aValues;
	}
	
	/**
	 * Set all the sets for this BigInt
	 *
	 * @param array $aValues
	 */
	private function setSets( $aValues ) {
		$this->aValues = $aValues;
	}
	
	/**
	 * Add a new set to this BigInt
	 * (Remember that it will be added to the front of the number, but the back of the array)
	 * 
	 * @param integer
	 */
	private function addSet( $iValue ) {
		$this->aValues[] = $iValue;
	}
	
	/**
	 * Return the length of this BigInt 
	 *
	 * return integer $iLength
	 */
	private function getLength() {
		return count( $this->aValues );
	}
	
	/**
	 * Return or output the value of the BigInt
	 * 
	 * @param boolean $bEcho, whether to echo the result or return it as a string 
	 */
	public function show( $bEcho = true ) {
		$bPad = false;
		if ( $bEcho ) {
			$aValues = $this->getSets();
			while ( ( $iValue = array_pop ( $aValues ) ) !== null ) {
				if ( $bPad ) {
					$iValue = str_pad( $iValue, self::$iChunkSize, '0', STR_PAD_LEFT );
				}
				echo $iValue;
				$bPad = true;
			}
		}
		else {
			$sReturn = '';
			$aValues = $this->getSets();
			while ( ( $iValue = array_pop ( $aValues ) ) !== null ) {
				if ( $bPad ) { 
					$iValue = str_pad( $iValue, self::$iChunkSize, '0', STR_PAD_LEFT );
				}
				$bPad = true;
				$sReturn .= $iValue;
			}
			return $sReturn;
		}
	}
	
}

Overigens lijkt het erop dat je de rest niet meeneemt helemaal aan het einde, dus als je 5 + 5 doet met jouw ding vermoed ik dat er 0 uitkomt, en niet 10... dat zou ik nog even controleren :)
 
Hmmm, dat hele OOP is nog ingewikkelder dan ik dacht :shocked:

Denk dat ik me daar eerst maar eens serieus in moet gaan verdiepen voordat ik een enigszins efficiente en handige class hiervoor kan schrijven.

Kun je me nog even vertellen wat het verschil is tussen:

PHP:
$this->iChuckMaxAmount

// en

self::$iChuckMaxAmount

Grr, ik wist dat er een moment zou komen waarop ik er niet meer omheen kon ;) Maar ik ga jouw class straks (of morgen) eens in detail doornemen, en als ik nog vragen heb dan hoor je het wel! ;)

Overigens klopt het dat de rest waarde de laatste loop niet mee genomen werd. :thumb:
 
$this->value is een lokale variabele, die gebonden is aan het object.

self::$value is een statische variabele, die gebonden is aan de klasse zelf.

Dus, als een object zijn lokale ->value veranderd, dan is dat alleen in die instantie.
Maar als een object een statische ::$value veranderd, wordt die wijziging doorgevoerd in alle objecten met die klasse.
 
Status
Niet open voor verdere reacties.

Nieuwste berichten

Terug
Bovenaan Onderaan