Generating random doable math problems swift
I started programming with Turbo Pascal and as Niklaus Wirth said: Algorithms + Data Structure = Programs. You need to define data structures appropriate for your program.
First, some basic data structures. (Swift enum is much more powerful than that in other languages)
enum MathElement : CustomStringConvertible {
case Integer(value: Int)
case Percentage(value: Int)
case Expression(expression: MathExpression)
var description: String {
switch self {
case .Integer(let value): return "\(value)"
case .Percentage(let percentage): return "\(percentage)%"
case .Expression(let expr): return expr.description
}
}
var nsExpressionFormatString : String {
switch self {
case .Integer(let value): return "\(value).0"
case .Percentage(let percentage): return "\(Double(percentage) / 100)"
case .Expression(let expr): return "(\(expr.description))"
}
}
}
enum MathOperator : String {
case plus = "+"
case minus = "-"
case multiply = "*"
case divide = "/"
static func random() -> MathOperator {
let allMathOperators: [MathOperator] = [.plus, .minus, .multiply, .divide]
let index = Int(arc4random_uniform(UInt32(allMathOperators.count)))
return allMathOperators[index]
}
}
Now the main class:
class MathExpression : CustomStringConvertible {
var lhs: MathElement
var rhs: MathElement
var `operator`: MathOperator
init(lhs: MathElement, rhs: MathElement, operator: MathOperator) {
self.lhs = lhs
self.rhs = rhs
self.operator = `operator`
}
var description: String {
var leftString = ""
var rightString = ""
if case .Expression(_) = lhs {
leftString = "(\(lhs))"
} else {
leftString = lhs.description
}
if case .Expression(_) = rhs {
rightString = "(\(rhs))"
} else {
rightString = rhs.description
}
return "\(leftString) \(self.operator.rawValue) \(rightString)"
}
var result : Any? {
let format = "\(lhs.nsExpressionFormatString) \(`operator`.rawValue) \(rhs.nsExpressionFormatString)"
let expr = NSExpression(format: format)
return expr.expressionValue(with: nil, context: nil)
}
static func random() -> MathExpression {
let lhs = MathElement.Integer(value: Int(arc4random_uniform(10)))
let rhs = MathElement.Integer(value: Int(arc4random_uniform(10)))
return MathExpression(lhs: lhs, rhs: rhs, operator: .random())
}
}
Usage:
let a = MathExpression(lhs: .Integer(value: 1), rhs: .Integer(value: 2), operator: .divide)
let b = MathExpression(lhs: .Integer(value: 10), rhs: .Percentage(value: 20), operator: .minus)
let c = MathExpression.random()
let d = MathExpression(lhs: .Integer(value: 1), rhs: .Integer(value: 2), operator: .plus)
let e = MathExpression(lhs: .Integer(value: 3), rhs: .Integer(value: 4), operator: .plus)
let f = MathExpression(lhs: .Expression(expression: d), rhs: .Expression(expression: e), operator: .multiply)
let x = MathExpression.random()
let y = MathExpression.random()
let z = MathExpression(lhs: .Expression(expression: x), rhs: .Expression(expression: y), operator: .plus)
print("a: \(a) = \(a.result!)")
print("b: \(b) = \(b.result!)")
print("c: \(c) = \(c.result!)")
print("f: \(f) = \(f.result!)") // the classic (1 + 2) * (3 + 4)
print("z: \(z) = \(z.result!)") // this is completely random
Generating a simple algebraic expression in swift
Since all you want is a string representing an equation and a value for x, you don't need to do any solving. Just start with x and transform it until you have a nice equation. Here's a sample: (copy and paste it into a Playground to try it out)
import UIKit
enum Operation: String {
case addition = "+"
case subtraction = "-"
case multiplication = "*"
case division = "/"
static func all() -> [Operation] {
return [.addition, .subtraction, .multiplication, .division]
}
static func random() -> Operation {
let all = Operation.all()
let selection = Int(arc4random_uniform(UInt32(all.count)))
return all[selection]
}
}
func addNewTerm(formula: String, result: Int) -> (formula: String, result: Int) {
// choose a random number and operation
let operation = Operation.random()
let number = chooseRandomNumberFor(operation: operation, on: result)
// apply to the left side
let newFormula = applyTermTo(formula: formula, number: number, operation: operation)
// apply to the right side
let newResult = applyTermTo(result: result, number: number, operation: operation)
return (newFormula, newResult)
}
func applyTermTo(formula: String, number:Int, operation:Operation) -> String {
return "\(formula) \(operation.rawValue) \(number)"
}
func applyTermTo(result: Int, number:Int, operation:Operation) -> Int {
switch(operation) {
case .addition: return result + number
case .subtraction: return result - number
case .multiplication: return result * number
case .division: return result / number
}
}
func chooseRandomNumberFor(operation: Operation, on number: Int) -> Int {
switch(operation) {
case .addition, .subtraction, .multiplication:
return Int(arc4random_uniform(10) + 1)
case .division:
// add code here to find integer factors
return 1
}
}
func generateFormula(_ numTerms:Int = 1) -> (String, Int) {
let x = Int(arc4random_uniform(10))
var leftSide = "x"
var result = x
for i in 1...numTerms {
(leftSide, result) = addNewTerm(formula: leftSide, result: result)
if i < numTerms {
leftSide = "(" + leftSide + ")"
}
}
let formula = "\(leftSide) = \(result)"
return (formula, x)
}
func printFormula(_ numTerms:Int = 1) {
let (formula, x) = generateFormula(numTerms)
print(formula, " x = ", x)
}
for i in 1...30 {
printFormula(Int(arc4random_uniform(3)) + 1)
}
There are some things missing. The sqrt() function will have to be implemented separately. And for division to be useful, you'll have to add in a system to find factors (since you presumably want the results to be integers). Depending on what sort of output you want, there's a lot more work to do, but this should get you started.
Here's sample output:
(x + 10) - 5 = 11 x = 6
((x + 6) + 6) - 1 = 20 x = 9
x - 2 = 5 x = 7
((x + 3) * 5) - 6 = 39 x = 6
(x / 1) + 6 = 11 x = 5
(x * 6) * 3 = 54 x = 3
x * 9 = 54 x = 6
((x / 1) - 6) + 8 = 11 x = 9
How to generate random numbers to satisfy a specific mean and median in python?
One way to get a result really close to what you want is to generate two separate random ranges with length 100 that satisfies your median constraints and includes all the desire range of numbers. Then by concatenating the arrays the mean will be around 12 but not quite equal to 12. But since it's just mean that you're dealing with you can simply generate your expected result by tweaking one of these arrays.
In [162]: arr1 = np.random.randint(2, 7, 100)
In [163]: arr2 = np.random.randint(7, 40, 100)
In [164]: np.mean(np.concatenate((arr1, arr2)))
Out[164]: 12.22
In [166]: np.median(np.concatenate((arr1, arr2)))
Out[166]: 6.5
Following is a vectorized and very much optimized solution against any other solution that uses for loops or python-level code by constraining the random sequence creation:
import numpy as np
import math
def gen_random():
arr1 = np.random.randint(2, 7, 99)
arr2 = np.random.randint(7, 40, 99)
mid = [6, 7]
i = ((np.sum(arr1 + arr2) + 13) - (12 * 200)) / 40
decm, intg = math.modf(i)
args = np.argsort(arr2)
arr2[args[-41:-1]] -= int(intg)
arr2[args[-1]] -= int(np.round(decm * 40))
return np.concatenate((arr1, mid, arr2))
Demo:
arr = gen_random()
print(np.median(arr))
print(arr.mean())
6.5
12.0
The logic behind the function:
In order for us to have a random array with that criteria we can concatenate 3 arrays together arr1
, mid
and arr2
. arr1
and arr2
each hold 99 items and the mid
holds 2 items 6 and 7 so that make the final result to give as 6.5 as the median. Now we an create two random arrays each with length 99. All we need to do to make the result to have a 12 mean is to find the difference between the current sum and 12 * 200
and subtract the result from our N largest numbers which in this case we can choose them from arr2
and use N=50
.
Edit:
If it's not a problem to have float numbers in your result you can actually shorten the function as following:
import numpy as np
import math
def gen_random():
arr1 = np.random.randint(2, 7, 99).astype(np.float)
arr2 = np.random.randint(7, 40, 99).astype(np.float)
mid = [6, 7]
i = ((np.sum(arr1 + arr2) + 13) - (12 * 200)) / 40
args = np.argsort(arr2)
arr2[args[-40:]] -= i
return np.concatenate((arr1, mid, arr2))
Find position far enough away
I dont' use unity, but I think this problem isn't unity/c# specific.
You can implement it considering that every planet you want to place "depends" from a parent, someway, sun excluded and placed at origin.
So said, sun it's located at (0,0,0), Planet1 belongs from sun and it's located at certain radius distance, so it has it's own radius and a rotation angle (always related to its parent).
Planet 2 may have sun as parent, so it's a "planet", or from Planet1, becoming its moon.
And so on...
With this simple scheme, you always have to manage 4 things which can be easly implemented as a class, i.e (not tested, I wrote it here without a compiler, check it from possible syntax errors):
public class Planet
{
public Planet Parent; //if null, I'm the first in chain
public float OrbitRadius; //From my parent
public float OrbitAngle; //0-360 around my parent
public float Radius; //Planet size
//EDIT:
public Vector3 Position;
public void CalculatePosition()
{
if(Parent == null) this.Position = new Vector(0, 0, 0);
this.Position = Parent.Position + OrbitRadius + Math.Sin(OrbitAngle * Math.PI / 180.0f); //Check if it has to be done for each vector component
}
}
More, you can easly implement sun, planets, moons, moons of moons :-)
Every time you create a planet, you will assign its parent, randomize class values taking care only of OrbitRadius >> Radius (>> I mean really "really greater", not just "greater")
This should also give you a strongest "planet chain" to deal with, maybe with recursive functions.
As final step, you can "render" their positions walking all the chain, going to add PlanetX position to parent's position(s) in an incremental manner, dealing only to OrbitAngle; you need some Sin/Cos math here but you will not loose with scale factors.
Hope this helps
edit: I added a sort of CalculatePosition, can't verify it now, It's only there to clarify the concept and can be surely improved.
If I called arc4random_uniform(6) at 5 o'clock OR 5:01, would I get the same number?
Your tester was probably thinking of the idea that software random number generators are in fact pseudo-random. Their output is not truly random as a physical process like a die roll would be: it's determined by some state that the generators hold or are given.
One simple implementation of a PRNG is a "linear congruential generator": the function rand()
in the standard library uses this technique. At its core, it is a straightforward mathematical function, and each output is generated by feeding in the previous one as input. It thus takes a "seed" value, and -- this is what your tester was thinking of -- the sequence of output values that you get is completely determined by the seed value.
If you create a simple C program using rand()
, you can (must, in fact) use the companion function srand()
(that's "seed rand") to give the LCG a starting value. If you use a constant as the seed value: srand(4)
, you will get the same values from rand()
, in the same order, every time.
One common way to get an arbitrary -- note, not random -- seed for rand()
is to use the current time: srand(time(NULL))
. If you did that, and re-seeded and generated a number fast enough that the return of time()
did not change, you would indeed see the same output from rand()
.
This doesn't apply to arc4random()
: it does not use an LCG, and it does not share this trait with rand()
. It was considered* "cryptographically secure"; that is, its output is indistinguishable from true, physical randomness.
This is partly due to the fact that arc4random()
re-seeds itself as you use it, and the seeding is itself based on unpredictable data gathered by the OS. The state that determines the output is entirely internal to the algorithm; as a normal user (i.e., not an attacker) you don't view, set, or otherwise interact with that state.
So no, the output of arc4random()
is not reliably repeatable by you. Pseudo-random algorithms which are repeatable do exist, however, and you can certainly use them for testing.
*Wikipedia notes that weaknesses have been found in the last few years, and that it may no longer be usable for cryptography. Should be fine for your game, though, as long as there's no money at stake!
Related Topics
Implement a Crosshair Kind Behaviour in Realitykit
Public Default Init in Protocol
Swift 3 Custom Extension of Ns Measurement? Ex. Sheeps to Goats
Does Realm Support Computed Property in Swift
Apple Mach-O Linker Error (Static, Not Ld)
How to Add UIpickerview in UIalertcontroller
How to Send Multiple Buttons in Button.Addtarget Action? Swift3
Leaks in Navigationview/List/Foreach with Dynamically Generated Views
How to Cache Cells and Also Reuse Cells in a Collectionview That Has Avplayers Embedded in Each Cell
Swift Sha256 Encryption Returns Different Encrypted String Compare to Objective C
Pass and Print All Cases in an Enum in Swift
Dynamic Links Firebase Function Not Being Called at All Xcode 12
Calling Consecutive Animations in Swift with Completion Handler
Swift Bug? Calling Super Class Method When Subclass with Generic Type
How to Find Multiple Nsrange for a String from Full String iOS Swift