Swift: find out how many digits an integer has - swift

I don't have the right English or math vocabulary to really explain what I want to do, but I'll try to explain. Basically I want to figure out "how big" an integer is, how many decimal positions it has. For example 1234 is "a thousand" number, and 2,987,123 is "a million" number.
I can do something like this, but that is rather silly :)
extension Int {
func size() -> Int {
switch self {
case 0...99:
return 10
case 100...999:
return 100
case 1000...9999:
return 1000
case 10000...99999:
return 10000
case 100000...999999:
return 100000
case 1000000...9999999:
return 1000000
default:
return 0 // where do we stop?
}
}
}

A solution using logarithms:
Note: This solution has limitations due to the inability of Double to fully represent the log10 of large Int converted to Double. It starts failing around 15
digits for Ints very close to the next power of 10 (e.g.
999999999999999).
This is a problem:
log10(Double(999999999999999)) == log10(Double(1000000000000000))
extension Int {
var size: Int {
self == 0 ? 1 : Int(pow(10.0, floor(log10(abs(Double(self))))))
}
}
A solution using Strings:
It avoids any mathematical representation errors by working entirely with Int and String.
extension Int {
var size: Int {
Int("1" + repeatElement("0", count: String(self.magnitude).count - 1))!
}
}
A generic version for any FixedWidthInteger:
In collaboration with #LeoDabus, I present the generic version for any integer type:
extension FixedWidthInteger {
var size: Self {
Self("1" + repeatElement("0", count: String(self.magnitude).count - 1))!
}
}
Examples:
Int8.max.size // 100
Int16.max.size // 10000
Int32.max.size // 1000000000
Int.max.size // 1000000000000000000
UInt.max.size // 10000000000000000000

I came up with this:
extension Int {
func size() -> Int {
var size = 1
var modifyingNumber = self
while modifyingNumber > 10 {
modifyingNumber = modifyingNumber / 10
size = size * 10
}
return size
}
}
Works, but it's rather imperative.

Is this too silly?
extension Int {
var size : Int {
String(self).count
}
}
My reasoning is that a "digit" really is a manner of writing, so converting to String answers the real question. Converting the size back to a number (i.e. the corresponding power of ten) would then of course lead right back to the logarithm answer. :)

Related

How to express 64-bit integers in Swift?

I'm doing an exercise which requires producing 64-bit positive integers in Swfit, but I have no idea how that can be achieved. My machine is 64-bit for sure, but my test code cannot even produce 63-bit prositive integers.
Using Double may solve the problem, but that's not what the exercise intends to be. Is there any solution for this issue? Thank you.
The test code is as follows:
import Foundation
func numberOfGrainsOnChessBoard () {
let ar = Array(1...64)
let arr = ar.map{twoMultipliedNTimes($0)}
var index = 1
for i in arr {
print("\(index): \(i)")
index = index + 1
}
}
func twoMultipliedNTimes (_ times: Int) -> UInt64 {
var product : UInt64 = 1;
for _ in 1...times {
product = product * 2
}
return product
}
addGrainsOnChessBoard()
The above code got an overflown error.
The code below will generate random Int64 bits integers between it's minimum and it's maximum value. So you can change the range to fit your needs.
let myInt: Int64 = Int64.random(in: Int64.min...Int64.max)

How to compare characters in Swift efficiently

I have a function in Swift that computes the hamming distance of two strings and then puts them into a connected graph if the result is 1.
For example, read to hear returns a hamming distance of 2 because read[0] != hear[0] and read[3] != hear[3].
At first, I thought my function was taking a long time because of the quantity of input (8,000+ word dictionary), but I knew that several minutes was too long. So, I rewrote my same algorithm in Java, and the computation took merely 0.3s.
I have tried writing this in Swift two different ways:
Way 1 - Substrings
extension String {
subscript (i: Int) -> String {
return self[Range(i ..< i + 1)]
}
}
private func getHammingDistance(w1: String, w2: String) -> Int {
if w1.length != w2.length { return -1 }
var counter = 0
for i in 0 ..< w1.length {
if w1[i] != w2[i] { counter += 1 }
}
return counter
}
Results: 434 seconds
Way 2 - Removing Characters
private func getHammingDistance(w1: String, w2: String) -> Int {
if w1.length != w2.length { return -1 }
var counter = 0
var c1 = w1, c2 = w2 // need to mutate
let length = w1.length
for i in 0 ..< length {
if c1.removeFirst() != c2.removeFirst() { counter += 1 }
}
return counter
}
Results: 156 seconds
Same Thing in Java
Results: 0.3 seconds
Where it's being called
var graph: Graph
func connectData() {
let verticies = graph.canvas // canvas is Array<Node>
// Node has key that holds the String
for vertex in 0 ..< verticies.count {
for compare in vertex + 1 ..< verticies.count {
if getHammingDistance(w1: verticies[vertex].key!, w2: verticies[compare].key!) == 1 {
graph.addEdge(source: verticies[vertex], neighbor: verticies[compare])
}
}
}
}
156 seconds is still far too inefficient for me. What is the absolute most efficient way of comparing characters in Swift? Is there a possible workaround for computing hamming distance that involves not comparing characters?
Edit
Edit 1: I am taking an entire dictionary of 4 and 5 letter words and creating a connected graph where the edges indicate a hamming distance of 1. Therefore, I am comparing 8,000+ words to each other to generate edges.
Edit 2: Added method call.
Unless you chose a fixed length character model for your strings, methods and properties such as .count and .characters will have a complexity of O(n) or at best O(n/2) (where n is the string length). If you were to store your data in an array of character (e.g. [Character] ), your functions would perform much better.
You can also combine the whole calculation in a single pass using the zip() function
let hammingDistance = zip(word1.characters,word2.characters)
.filter{$0 != $1}.count
but that still requires going through all characters of every word pair.
...
Given that you're only looking for Hamming distances of 1, there is a faster way to get to all the unique pairs of words:
The strategy is to group words by the 4 (or 5) patterns that correspond to one "missing" letter. Each of these pattern groups defines a smaller scope for word pairs because words in different groups would be at a distance other than 1.
Each word will belong to as many groups as its character count.
For example :
"hear" will be part of the pattern groups:
"*ear", "h*ar", "he*r" and "hea*".
Any other word that would correspond to one of these 4 pattern groups would be at a Hamming distance of 1 from "hear".
Here is how this can be implemented:
// Test data 8500 words of 4-5 characters ...
var seenWords = Set<String>()
var allWords = try! String(contentsOfFile: "/usr/share/dict/words")
.lowercased()
.components(separatedBy:"\n")
.filter{$0.characters.count == 4 || $0.characters.count == 5}
.filter{seenWords.insert($0).inserted}
.enumerated().filter{$0.0 < 8500}.map{$1}
// Compute patterns for a Hamming distance of 1
// Replace each letter position with "*" to create patterns of
// one "non-matching" letter
public func wordH1Patterns(_ aWord:String) -> [String]
{
var result : [String] = []
let fullWord : [Character] = aWord.characters.map{$0}
for index in 0..<fullWord.count
{
var pattern = fullWord
pattern[index] = "*"
result.append(String(pattern))
}
return result
}
// Group words around matching patterns
// and add unique pairs from each group
func addHamming1Edges()
{
// Prepare pattern groups ...
//
var patternIndex:[String:Int] = [:]
var hamming1Groups:[[String]] = []
for word in allWords
{
for pattern in wordH1Patterns(word)
{
if let index = patternIndex[pattern]
{
hamming1Groups[index].append(word)
}
else
{
let index = hamming1Groups.count
patternIndex[pattern] = index
hamming1Groups.append([word])
}
}
}
// add edge nodes ...
//
for h1Group in hamming1Groups
{
for (index,sourceWord) in h1Group.dropLast(1).enumerated()
{
for targetIndex in index+1..<h1Group.count
{ addEdge(source:sourceWord, neighbour:h1Group[targetIndex]) }
}
}
}
On my 2012 MacBook Pro, the 8500 words go through 22817 (unique) edge pairs in 0.12 sec.
[EDIT] to illustrate my first point, I made a "brute force" algorithm using arrays of characters instead of Strings :
let wordArrays = allWords.map{Array($0.unicodeScalars)}
for i in 0..<wordArrays.count-1
{
let word1 = wordArrays[i]
for j in i+1..<wordArrays.count
{
let word2 = wordArrays[j]
if word1.count != word2.count { continue }
var distance = 0
for c in 0..<word1.count
{
if word1[c] == word2[c] { continue }
distance += 1
if distance > 1 { break }
}
if distance == 1
{ addEdge(source:allWords[i], neighbour:allWords[j]) }
}
}
This goes through the unique pairs in 0.27 sec. The reason for the speed difference is the internal model of Swift Strings which is not actually an array of equal length elements (characters) but rather a chain of varying length encoded characters (similar to the UTF model where special bytes indicate that the following 2 or 3 bytes are part of a single character. There is no simple Base+Displacement indexing of such a structure which must always be iterated from the beginning to get to the Nth element.
Note that I used unicodeScalars instead of Character because they are 16 bit fixed length representations of characters that allow a direct binary comparison. The Character type isn't as straightforward and take longer to compare.
Try this:
extension String {
func hammingDistance(to other: String) -> Int? {
guard self.characters.count == other.characters.count else { return nil }
return zip(self.characters, other.characters).reduce(0) { distance, chars in
distance + (chars.0 == chars.1 ? 0 : 1)
}
}
}
print("read".hammingDistance(to: "hear")) // => 2
The following code executed in 0.07 secounds for 8500 characters:
func getHammingDistance(w1: String, w2: String) -> Int {
if w1.characters.count != w2.characters.count {
return -1
}
let arr1 = Array(w1.characters)
let arr2 = Array(w2.characters)
var counter = 0
for i in 0 ..< arr1.count {
if arr1[i] != arr2[i] { counter += 1 }
}
return counter
}
After some messing around, I found a faster solution to #Alexander's answer (and my previous broken answer)
extension String {
func hammingDistance(to other: String) -> Int? {
guard !self.isEmpty, !other.isEmpty, self.characters.count == other.characters.count else {
return nil
}
var w1Iterator = self.characters.makeIterator()
var w2Iterator = other.characters.makeIterator()
var distance = 0;
while let w1Char = w1Iterator.next(), let w2Char = w2Iterator.next() {
distance += (w1Char != w2Char) ? 1 : 0
}
return distance
}
}
For comparing strings with a million characters, on my machine it's 1.078 sec compared to 1.220 sec, so roughly a 10% improvement. My guess is this is due to avoiding .zip and the slight overhead of .reduce and tuples
As others have noted, calling .characters repeatedly takes time. If you convert all of the strings once, it should help.
func connectData() {
let verticies = graph.canvas // canvas is Array<Node>
// Node has key that holds the String
// Convert all of the keys to utf16, and keep them
let nodesAsUTF = verticies.map { $0.key!.utf16 }
for vertex in 0 ..< verticies.count {
for compare in vertex + 1 ..< verticies.count {
if getHammingDistance(w1: nodesAsUTF[vertex], w2: nodesAsUTF[compare]) == 1 {
graph.addEdge(source: verticies[vertex], neighbor: verticies[compare])
}
}
}
}
// Calculate the hamming distance of two UTF16 views
func getHammingDistance(w1: String.UTF16View, w2: String.UTF16View) -> Int {
if w1.count != w2.count {
return -1
}
var counter = 0
for i in w1.startIndex ..< w1.endIndex {
if w1[i] != w1[i] {
counter += 1
}
}
return counter
}
I used UTF16, but you might want to try UTF8 depending on the data. Since I don't have the dictionary you are using, please let me know the result!
*broken*, see new answer
My approach:
private func getHammingDistance(w1: String, w2: String) -> Int {
guard w1.characters.count == w2.characters.count else {
return -1
}
let countArray: Int = w1.characters.indices
.reduce(0, {$0 + (w1[$1] == w2[$1] ? 0 : 1)})
return countArray
}
comparing 2 strings of 10,000 random characters took 0.31 seconds
To expand a bit: it should only require one iteration through the strings, adding as it goes.
Also it's way more concise 🙂.

How to split or iterate over an Int without converting to String in Swift [duplicate]

This question already has answers here:
Break A Number Up To An Array of Individual Digits
(6 answers)
Closed 5 years ago.
I was wondering if there was a way in Swift to split an Int up into it's individual digits without converting it to a String. For example:
let x: Int = 12345
//Some way to loop/iterate over x's digits
//Then map each digit in x to it's String value
//Return "12345"
For a bit of background, I'm attempting to create my own method of converting an Int to a String without using the String description property or using String Interpolation.
I've found various articles on this site but all the ones I've been able to find either start with a String or end up using the String description property to convert the Int to a String.
Thanks.
Just keep dividing by 10 and take the remainder:
extension Int {
func digits() -> [Int] {
var digits: [Int] = []
var num = self
repeat {
digits.append(num % 10)
num /= 10
} while num != 0
return digits.reversed()
}
}
x.digits() // [1,2,3,4,5]
Note that this will return all negative digits if the value is negative. You could add a special case if you want to handle that differently. This return [0] for 0, which is probably what you want.
And because everyone like pure functional programming, you can do it that way too:
func digits() -> [Int] {
let partials = sequence(first: self) {
let p = $0 / 10
guard p != 0 else { return nil }
return p
}
return partials.reversed().map { $0 % 10 }
}
(But I'd probably just use the loop here. I find sequence too tricky to reason about in most cases.)
A recursive way...
extension Int {
func createDigitArray() -> [Int] {
if self < 10 {
return [self]
} else {
return (self / 10).createDigitArray() + [self % 10]
}
}
}
12345.createDigitArray() //->[1, 2, 3, 4, 5]
A very easy approach would be using this function:
func getDigits(of number: Int) -> [Int] {
var digits = [Int]()
var x = number
repeat{
digits.insert(abs(x % 10), at: 0)
x/=10
} while x != 0
return digits
}
And using it like this:
getDigits(of: 97531) // [9,7,5,3,1]
getDigits(of: -97531) // [9,7,5,3,1]
As you can see, for a negative number you will receive the array of its digits, but at their absolute value (e.g.: -9 => 9 and -99982 => 99982)
Hope it helps!

How to replace a complicated C-style for loop in Swift 2.2

For an unsigned integer type library that I've developed, I have a specialized C-style for loop used for calculating the significant bits in a stored numeric value. I have been struggling for some time with how to convert this into a Swift 2.2+ style for loop. Here's the code in question:
/// Counts up the significant bits in stored data.
public var significantBits: UInt128 {
// Will turn into final result.
var significantBitCount: UInt128 = 0
// The bits to crawl in loop.
var bitsToWalk: UInt64 = 0
if self.value.upperBits > 0 {
bitsToWalk = self.value.upperBits
// When upperBits > 0, lowerBits are all significant.
significantBitCount += 64
} else if self.value.lowerBits > 0 {
bitsToWalk = self.value.lowerBits
}
if bitsToWalk > 0 {
// Walk significant bits by shifting right until all bits are equal to 0.
for var bitsLeft = bitsToWalk; bitsLeft > 0; bitsLeft >>= 1 {
significantBitCount += 1
}
}
return significantBitCount
}
I'm sure there are multiple ways to handle this, and I can think of some more verbose means of handling this, but I'm interested in finding a succinct way of handling this scenario that I can reapply to similar circumstances. I find that I very rarely use C-style for loops, but when I do, it's for bizarre scenarios like this one where it's the most succinct way to handle a problem.
The simplest solution is to just use a while loop:
Replace this code:
if bitsToWalk > 0 {
// Walk significant bits by shifting right until all bits are equal to 0.
for var bitsLeft = bitsToWalk; bitsLeft > 0; bitsLeft >>= 1 {
significantBitCount += 1
}
}
With the following while loop:
while bitsToWalk > 0 {
significantBitCount += 1
bitsToWalk >>= 1
}
One option is to use the built-in processor functions:
Put:
#import <x86intrin.h>
to your Obj-C bridging header and then in Swift:
let number: UInt64 = 111
let mostSignificantBit = _lzcnt_u64(number)
print(mostSignificantBit)
(Of course, you have to be on the correct architecture, this function is defined only on x86. This solution is not exactly well portable).
This function should calculate the number of significant bits in a UInt64 value:
import Foundation
func significantBits(n: UInt64) -> Int {
return Int(ceil(log2(Double(n))))
}
let n: UInt64 = 0xFFFFFFFFFFFFFFFF // 64 significant bits
let m: UInt64 = 0b11011 // 5 significant bits
print("n has \(significantBits(n)) significant bits.")
print("m has \(significantBits(m)) significant bits.")
and outputs:
n has 64 significant bits.
m has 5 significant bits.
You could probably replace your code with something like:
private func calcSigBits(n: UInt64) -> Int {
return Int(ceil(log2(Double(n))))
}
public var significantBits: Int {
if self.value.upperBits > 0 {
return calcSigBits(self.value.upperBits) + 64
}
else {
return calcSigBits(self.value.lowerBits)
}
}
If you don't want to use log2, you can use the loop from nhgrif's answer, but it's still good to refactor this out, since it's a conceptually separate operation, and makes your own code much simpler. You could even add it as an extension to UInt64:
extension UInt64 {
public var significantBits: Int {
var sb = 0
var value = self
while value > 0 {
sb += 1
value >>= 1
}
return sb
}
}
// Rest of your class definition...
public var significantBits: Int {
if self.value.upperBits > 0 {
return self.value.upperBits.significantBits + 64
}
else {
return self.value.lowerBits.significantBits
}
}

Creating random Bool in Swift

I'm needing to create a random bool value in my game, in Swift.
It's basically, if Yes (or 1), spawn one object, if No (or 0), spawn the other.
So far, looking at this question and a similar one on here, I found this:
let randomSequenceNumber = Int(arc4random_uniform(2))
Now it works, but it seems bias to 0 to me... like ridiculously bias...
This is how I'm then using the value:
if(randomSequenceNumber == 0)
//spawn object A
else
//spawn object B
Is there a better way to implement this using a random bool value? That isn't bias to a certain value?
Update
Bit of an experiment to see how many 1's vs 0's were were generated in 10,000 calls:
func experiment() {
var numbers: [Int] = []
var tester: Int = 0
var sum = 0
for i in 0...10000 {
tester = Int(arc4random_uniform(2))
numbers.append(tester)
print(i)
}
for number in numbers {
sum += number
}
print("Total 1's: ", sum)
}
Test 1: Console Output: Total 1's: 4936
Test 2: Console Output: Total 1's: 4994
Test 3: Console Output: Total 1's: 4995
Xcode 10 with Swift 4.2
Looks like Apple  engineers are listening
let randomBool = Bool.random()
import Foundation
func randomBool() -> Bool {
return arc4random_uniform(2) == 0
}
for i in 0...10 {
print(randomBool())
}
for more advanced generator the theory is available here
for basic understanding of Bernoulli (or binomial) distribution check here
extension Bool {
static func random() -> Bool {
return arc4random_uniform(2) == 0
}
}
// usage:
Bool.random()