How to resolve the algorithm First perfect square in base n with n unique digits step by step in the Kotlin programming language

Published on 22 June 2024 08:30 PM

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 the ALPHABET string.
  • toStr(b): Converts a BigInteger b to a string representation in the current base.
  • allInQS(b): Checks if a portion of digits in a BigInteger b contains all unique digits, returning true if yes, and false otherwise.
  • allInS(b): Checks if all digits in a BigInteger b contain all unique digits, returning true if yes, and false otherwise.
  • allInQ(b): Checks if a portion of digits in a BigInteger b contains all unique digits, returning true if yes, and false otherwise.
  • allIn(b): Checks if all digits in a BigInteger b contain all unique digits, returning true if yes, and false otherwise.
  • to10(s): Converts a string s 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 square sq 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 and rt 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