How to resolve the algorithm First perfect square in base n with n unique digits step by step in the Kotlin programming language
How to resolve the algorithm First perfect square in base n with n unique digits step by step in the Kotlin programming language
Table of Contents
Problem Statement
Find the first perfect square in a given base N that has at least N digits and exactly N significant unique digits when expressed in base N. E.G. In base 10, the first perfect square with at least 10 unique digits is 1026753849 (32043²). You may use analytical methods to reduce the search space, but the code must do a search. Do not use magic numbers or just feed the code the answer to verify it is correct.
Let's start with the solution:
Step by Step solution about How to resolve the algorithm First perfect square in base n with n unique digits step by step in the Kotlin programming language
General Overview
The provided Kotlin code is designed to perform certain mathematical calculations and optimizations related to finding squares of integers in various bases. It uses various algorithms and data structures to efficiently determine the square root of numbers in a range of bases and measure the time taken for these calculations.
Detailed Explanation
1. Constants and Variables:
ALPHABET
: A string containing the characters used for representing numbers in different bases.base
: The current base being processed.bmo
: The upper limit of bases for which calculations are performed (27 in this case).blim
: The index of the last digit in the minimum value string.ic
: The number of digits in the minimum value string.st0
: The start time of the entire program.bllim
: A BigInteger object representing the upper limit for the digits being checked.threshold
: A BigInteger object representing the threshold for checking if a digit set is complete.hs
: A mutable set used for storing digits.o
: A mutable set used for storing digits.chars
: An array of characters used for representing numbers in different bases.limits
: A mutable list of BigInteger objects representing the upper limits for the digits being checked in each base.ms
: A string representing the minimum value in the current base.
2. Helper Functions:
indexOf(c)
: Returns the index of a character in theALPHABET
string.toStr(b)
: Converts a BigIntegerb
to a string representation in the current base.allInQS(b)
: Checks if a portion of digits in a BigIntegerb
contains all unique digits, returningtrue
if yes, andfalse
otherwise.allInS(b)
: Checks if all digits in a BigIntegerb
contain all unique digits, returningtrue
if yes, andfalse
otherwise.allInQ(b)
: Checks if a portion of digits in a BigIntegerb
contains all unique digits, returningtrue
if yes, andfalse
otherwise.allIn(b)
: Checks if all digits in a BigIntegerb
contain all unique digits, returningtrue
if yes, andfalse
otherwise.to10(s)
: Converts a strings
to a BigInteger in base 10.fixup(n)
: Returns a string representing the minimum value in the current base with an optional extra digit.check(sq)
: Checks the squaresq
against the threshold and updates limits if necessary.format(d)
: Formats a Duration object into a string.
3. Main Calculation Loop:
- The
doOne
function performs the main calculations for each base. - It calculates the minimum value string,
ms
, and the square root of the minimum value,rt
. - It then enters a loop to check if the square of the current root,
sq
, is within the limits for the current base. - If the square is within the limits, it updates the
limits
list and continues the loop. - If the square goes beyond the limits, it reduces the size of the minimum value string and adjusts the limits.
- It repeatedly updates the
sq
andrt
values until it finds the square root of the minimum value within the specified limits. - It keeps track of the number of iterations and the time taken for each base.
4. Output:
- The program prints the base, the increment used in the calculations, the first digit of the minimum value (if present), the square root of the minimum value, the square of the minimum value, the number of iterations performed, the time taken for the current base, and the total time taken so far.
Usage:
This program can be used to study the behavior of finding squares of integers in different bases, experimenting with different bases and calculation methods to optimize the performance of such calculations.
Source code in the kotlin programming language
import java.math.BigInteger
import java.time.Duration
import java.util.ArrayList
import java.util.HashSet
import kotlin.math.sqrt
const val ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz|"
var base: Byte = 0
var bmo: Byte = 0
var blim: Byte = 0
var ic: Byte = 0
var st0: Long = 0
var bllim: BigInteger? = null
var threshold: BigInteger? = null
var hs: MutableSet<Byte> = HashSet()
var o: MutableSet<Byte> = HashSet()
val chars = ALPHABET.toCharArray()
var limits: MutableList<BigInteger?>? = null
var ms: String? = null
fun indexOf(c: Char): Int {
for (i in chars.indices) {
if (chars[i] == c) {
return i
}
}
return -1
}
// convert BigInteger to string using current base
fun toStr(b: BigInteger): String {
var b2 = b
val bigBase = BigInteger.valueOf(base.toLong())
val res = StringBuilder()
while (b2 > BigInteger.ZERO) {
val divRem = b2.divideAndRemainder(bigBase)
res.append(chars[divRem[1].toInt()])
b2 = divRem[0]
}
return res.toString()
}
// check for a portion of digits, bailing if uneven
fun allInQS(b: BigInteger): Boolean {
var b2 = b
val bigBase = BigInteger.valueOf(base.toLong())
var c = ic.toInt()
hs.clear()
hs.addAll(o)
while (b2 > bllim) {
val divRem = b2.divideAndRemainder(bigBase)
hs.add(divRem[1].toByte())
c++
if (c > hs.size) {
return false
}
b2 = divRem[0]
}
return true
}
// check for a portion of digits, all the way to the end
fun allInS(b: BigInteger): Boolean {
var b2 = b
val bigBase = BigInteger.valueOf(base.toLong())
hs.clear()
hs.addAll(o)
while (b2 > bllim) {
val divRem = b2.divideAndRemainder(bigBase)
hs.add(divRem[1].toByte())
b2 = divRem[0]
}
return hs.size == base.toInt()
}
// check for all digits, bailing if uneven
fun allInQ(b: BigInteger): Boolean {
var b2 = b
val bigBase = BigInteger.valueOf(base.toLong())
var c = 0
hs.clear()
while (b2 > BigInteger.ZERO) {
val divRem = b2.divideAndRemainder(bigBase)
hs.add(divRem[1].toByte())
c++
if (c > hs.size) {
return false
}
b2 = divRem[0]
}
return true
}
// check for all digits, all the way to the end
fun allIn(b: BigInteger): Boolean {
var b2 = b
val bigBase = BigInteger.valueOf(base.toLong())
hs.clear()
while (b2 > BigInteger.ZERO) {
val divRem = b2.divideAndRemainder(bigBase)
hs.add(divRem[1].toByte())
b2 = divRem[0]
}
return hs.size == base.toInt()
}
// parse a string into a BigInteger, using current base
fun to10(s: String?): BigInteger {
val bigBase = BigInteger.valueOf(base.toLong())
var res = BigInteger.ZERO
for (element in s!!) {
val idx = indexOf(element)
val bigIdx = BigInteger.valueOf(idx.toLong())
res = res.multiply(bigBase).add(bigIdx)
}
return res
}
// returns the minimum value string, optionally inserting extra digit
fun fixup(n: Int): String {
var res = ALPHABET.substring(0, base.toInt())
if (n > 0) {
val sb = StringBuilder(res)
sb.insert(n, n)
res = sb.toString()
}
return "10" + res.substring(2)
}
// checks the square against the threshold, advances various limits when needed
fun check(sq: BigInteger) {
if (sq > threshold) {
o.remove(indexOf(ms!![blim.toInt()]).toByte())
blim--
ic--
threshold = limits!![bmo - blim - 1]
bllim = to10(ms!!.substring(0, blim + 1))
}
}
// performs all the calculations for the current base
fun doOne() {
limits = ArrayList()
bmo = (base - 1).toByte()
var dr: Byte = 0
if ((base.toInt() and 1) == 1) {
dr = (base.toInt() shr 1).toByte()
}
o.clear()
blim = 0
var id: Byte = 0
var inc = 1
val st = System.nanoTime()
val sdr = ByteArray(bmo.toInt())
var rc: Byte = 0
for (i in 0 until bmo) {
sdr[i] = (i * i % bmo).toByte()
if (sdr[i] == dr) {
rc = (rc + 1).toByte()
}
if (sdr[i] == 0.toByte()) {
sdr[i] = (sdr[i] + bmo).toByte()
}
}
var i: Long = 0
if (dr > 0) {
id = base
i = 1
while (i <= dr) {
if (sdr[i.toInt()] >= dr) {
if (id > sdr[i.toInt()]) {
id = sdr[i.toInt()]
}
}
i++
}
id = (id - dr).toByte()
i = 0
}
ms = fixup(id.toInt())
var sq = to10(ms)
var rt = BigInteger.valueOf((sqrt(sq.toDouble()) + 1).toLong())
sq = rt.multiply(rt)
if (base > 9) {
for (j in 1 until base) {
limits!!.add(to10(ms!!.substring(0, j) + chars[bmo.toInt()].toString().repeat(base - j + if (rc > 0) 0 else 1)))
}
limits!!.reverse()
while (sq < limits!![0]) {
rt = rt.add(BigInteger.ONE)
sq = rt.multiply(rt)
}
}
var dn = rt.shiftLeft(1).add(BigInteger.ONE)
var d = BigInteger.ONE
if (base > 3 && rc > 0) {
while (sq.remainder(BigInteger.valueOf(bmo.toLong())).compareTo(BigInteger.valueOf(dr.toLong())) != 0) {
rt = rt.add(BigInteger.ONE)
sq = sq.add(dn)
dn = dn.add(BigInteger.TWO)
} // aligns sq to dr
inc = bmo / rc
if (inc > 1) {
dn = dn.add(rt.multiply(BigInteger.valueOf(inc - 2.toLong())).subtract(BigInteger.ONE))
d = BigInteger.valueOf(inc * inc.toLong())
}
dn = dn.add(dn).add(d)
}
d = d.shiftLeft(1)
if (base > 9) {
blim = 0
while (sq < limits!![bmo - blim - 1]) {
blim++
}
ic = (blim + 1).toByte()
threshold = limits!![bmo - blim - 1]
if (blim > 0) {
for (j in 0..blim) {
o.add(indexOf(ms!![j]).toByte())
}
}
bllim = if (blim > 0) {
to10(ms!!.substring(0, blim + 1))
} else {
BigInteger.ZERO
}
if (base > 5 && rc > 0) while (!allInQS(sq)) {
sq = sq.add(dn)
dn = dn.add(d)
i += 1
check(sq)
} else {
while (!allInS(sq)) {
sq = sq.add(dn)
dn = dn.add(d)
i += 1
check(sq)
}
}
} else {
if (base > 5 && rc > 0) {
while (!allInQ(sq)) {
sq = sq.add(dn)
dn = dn.add(d)
i += 1
}
} else {
while (!allIn(sq)) {
sq = sq.add(dn)
dn = dn.add(d)
i += 1
}
}
}
rt = rt.add(BigInteger.valueOf(i * inc))
val delta1 = System.nanoTime() - st
val dur1 = Duration.ofNanos(delta1)
val delta2 = System.nanoTime() - st0
val dur2 = Duration.ofNanos(delta2)
System.out.printf(
"%3d %2d %2s %20s -> %-40s %10d %9s %9s\n",
base, inc, if (id > 0) ALPHABET.substring(id.toInt(), id + 1) else " ", toStr(rt), toStr(sq), i, format(dur1), format(dur2)
)
}
private fun format(d: Duration): String {
val minP = d.toMinutesPart()
val secP = d.toSecondsPart()
val milP = d.toMillisPart()
return String.format("%02d:%02d.%03d", minP, secP, milP)
}
fun main() {
println("base inc id root square test count time total")
st0 = System.nanoTime()
base = 2
while (base < 28) {
doOne()
++base
}
}
You may also check:How to resolve the algorithm Terminal control/Display an extended character step by step in the Pascal programming language
You may also check:How to resolve the algorithm Shell one-liner step by step in the Icon and Unicon programming language
You may also check:How to resolve the algorithm Formatted numeric output step by step in the 11l programming language
You may also check:How to resolve the algorithm FizzBuzz step by step in the BBC BASIC programming language
You may also check:How to resolve the algorithm Sphenic numbers step by step in the Java programming language