Calculating optimal matrix size to fit N elements

Share your advanced PureBasic knowledge/code with the community.
User avatar
Lunasole
Addict
Addict
Posts: 1091
Joined: Mon Oct 26, 2015 2:55 am
Location: UA
Contact:

Calculating optimal matrix size to fit N elements

Post by Lunasole »

Hi once more. Yesterday I needed such code and remembered I've already wrote it many years ago, so here it is reanimated and ported to PB ^^
Rarely but might be useful. Also I don't know how exactly such algorithm is called (nor exists it or not).

Code: Select all

EnableExplicit

; (c) Lunasole 2000x-don't-remember-exactly
; Value			a number to fit, can be >= 0
; RETURN:		none, the X and Y sizes of matrix returned byref
Procedure AmountToMatrix (Value, *OutX.Integer, *OutY.Integer)
	Protected.d SamplesDiff ; can use integer instead, result will be "more square" but less optimal
	*OutY\i = Int(Sqr(Value))
	*OutX\i = *OutY\i

	If Pow(*OutY\i, 2) < Value
		SamplesDiff = 0.4 + (Value - Pow(*OutY\i, 2)) / *OutY\i
	    If Mod(SamplesDiff, 2) = 0
	        *OutX\i = *OutY\i + SamplesDiff / 2
	        *OutY\i = *OutY\i + SamplesDiff / 2
	    Else
	        *OutX\i = *OutY\i + SamplesDiff
	    EndIf
	EndIf
EndProcedure


; Example
; assume you have 5000 files
Define inFilesCnt = 5000
; and need to place them using box-like order for some reason ^^
Define SamplesX, SamplesY
; that thing does something like that
AmountToMatrix(inFilesCnt, @SamplesX, @SamplesY)

Debug Str(inFilesCnt) + " files may be ordered using " + Str(SamplesX) + "x" + Str(SamplesY) + " matrix"
"W̷i̷s̷h̷i̷n̷g o̷n a s̷t̷a̷r"
User avatar
Lunasole
Addict
Addict
Posts: 1091
Joined: Mon Oct 26, 2015 2:55 am
Location: UA
Contact:

Re: Calculating optimal matrix size to fit N elements

Post by Lunasole »

Here is slightly extended version, it allows to define one of sizes manually, also optional UseInt switch changes auto-calculation behavior:

Code: Select all

EnableExplicit
; Value			a number to fit, can be >= 0
; *OutX, OutY	in/out values. input must be zero to be calculated
; UseInt		if true, use rounding to estimate diff (this affects results on auto-calculation)
; RETURN:		none, the X and Y sizes of matrix returned byref
Procedure AmountToMatrix (Value, *OutX.Integer, *OutY.Integer, UseInt = #False)
	Protected.d SamplesDiff
	
	If *OutX\i = 0 And *OutY\i = 0 ; both X and Y are 0
		*OutY\i = Int(Sqr(Value))
		*OutX\i = *OutY\i
	
		If Pow(*OutY\i, 2) < Value
			If UseInt
				SamplesDiff = Round(0.4 + (Value - Pow(*OutY\i, 2)) / *OutY\i, #PB_Round_Nearest)
			Else
				SamplesDiff = 0.4 + (Value - Pow(*OutY\i, 2)) / *OutY\i
			EndIf
		    If Mod(SamplesDiff, 2) = 0
		        *OutX\i = *OutY\i + SamplesDiff / 2
		        *OutY\i = *OutY\i + SamplesDiff / 2
		    Else
		        *OutX\i = *OutY\i + SamplesDiff
		    EndIf
		EndIf
		
	ElseIf *OutX\i And Not *OutY\i ; user defined X size, but not Y
		*OutY\i = Round(Value / *OutX\i, #PB_Round_Up)
	ElseIf *OutY\i And Not *OutX\i ; user defined Y size, but not X
		*OutX\i = Round(Value / *OutY\i, #PB_Round_Up)
	EndIf
EndProcedure


; Example
; assume you have 5000 files
Define inFilesCnt = 5000
; and need to place them using box-like order for some reason ^^
Define SamplesX = 0, SamplesY = 0
; that thing does something like that
AmountToMatrix(inFilesCnt, @SamplesX, @SamplesY, #False)

Debug Str(inFilesCnt) + " files may be ordered using " + Str(SamplesX) + "x" + Str(SamplesY) + " matrix"
"W̷i̷s̷h̷i̷n̷g o̷n a s̷t̷a̷r"
User avatar
blueb
Addict
Addict
Posts: 1116
Joined: Sat Apr 26, 2003 2:15 pm
Location: Cuernavaca, Mexico

Re: Calculating optimal matrix size to fit N elements

Post by blueb »

Not exactly elegant, but would't this work?...

Code: Select all

; ... and need to place them using box-like order for some reason.
FileNumber = 5000
result.i = Round(Sqr(FileNumber), #PB_Round_Up)
; test
Debug result
Debug result * result
- It was too lonely at the top.

System : PB 6.21(x64) and Win 11 Pro (x64)
Hardware: AMD Ryzen 9 5900X w/64 gigs Ram, AMD RX 6950 XT Graphics w/16gigs Mem
User avatar
Lunasole
Addict
Addict
Posts: 1091
Joined: Mon Oct 26, 2015 2:55 am
Location: UA
Contact:

Re: Calculating optimal matrix size to fit N elements

Post by Lunasole »

blueb wrote:Not exactly elegant, but would't this work?...
That simplified variant works too, but shows wrong results for some values.
For example if filenumber = 5 it will return 3x3 matrix, while my code 3x2
"W̷i̷s̷h̷i̷n̷g o̷n a s̷t̷a̷r"
#NULL
Addict
Addict
Posts: 1499
Joined: Thu Aug 30, 2007 11:54 pm
Location: right here

Re: Calculating optimal matrix size to fit N elements

Post by #NULL »

using the code by blueb with an additional step to reduce height by 1 if possible:

Code: Select all

For FileNumber = 1 To 100
  
  resultX = Round(Sqr(FileNumber), #PB_Round_Up)
  resultY = resultX
  If (resultX * resultY) > FileNumber
    If (resultX * (resultY-1)) >= FileNumber
      resultY = resultY - 1
    EndIf
  EndIf
  
  Debug "-----------------------"
  Debug "" + FileNumber + " : " + resultX + " x " + resultY + " = " + Str(resultX * resultY)
  For n=1 To FileNumber
    s.s + "#"
    If n % resultX = 0  Or  n = FileNumber
      Debug s
      s = ""
    EndIf
  Next
  Debug ""
Next

#NULL
Addict
Addict
Posts: 1499
Joined: Thu Aug 30, 2007 11:54 pm
Location: right here

Re: Calculating optimal matrix size to fit N elements

Post by #NULL »

i just realized that you can do it also without the IFs :D
if the decimal part of Sqr(n) is > 0.5 then x and y need to be the same (rounded up) and if the decimal part is > 0.0 and <= 0.5 then you can reduce one of the dimensions.
you can achive this easily by using round-up for one dimension and round-nearest for the other one.

Code: Select all

For FileNumber = 1 To 100
  
  sqr.f = Sqr(FileNumber)
  resultX = Round(sqr, #PB_Round_Up)
  resultY = Round(sqr, #PB_Round_Nearest)
  
  Debug "-----------------------"
  Debug "" + FileNumber + " : " + resultX + " x " + resultY + " = " + Str(resultX * resultY)
  For n=1 To FileNumber
    s.s + "#"
    If n % resultX = 0  Or  n = FileNumber
      Debug s
      s = ""
    EndIf
  Next
  Debug ""
Next
i can't prove this mathematically but it seems to work :)
Post Reply