I make Wind direction enum and I want to make a range that catches all values between 2 numbers OR third one:
number between (0.0 and 45.0) OR (360.0)
enum class WindDirections(val description: String,val shortName : String, val range: ClosedFloatingPointRange<Double>,val image: Int) {
// North
NORTH("N North", "N" ,(0.0..45.0), R.drawable.wind_north_red50),
NORTH_EAST("NE North-East", "NE" ,45.1..90.0, R.drawable.wind_north_east_red),
(... East, South, West etc)
NOT_FOUND ("", "-",-1.1..-1.0,0); // error one, now 360* is inside that
(...)
With these functions, I want to get enum class - WindDirections with the right value, based on given angle windDirection: Double. For example:
The angle between 0 and 45 (0.0..45.0) returns WindDirections.North (Now want to make when its 360 to return same)
The angle between 45.1 and 90 (45.1..90.0) returns WindDirections.NORTH_EAST
companion object {
fun getDescriptionStringByDouble(windDirection: Double): String {
return getWindDirectionByDouble(windDirection).description
}
fun getShortDescriptionStringByDouble(windDirection: Double):String {
return getWindDirectionByDouble(windDirection).shortName
}
I want to get North for a range of 0..45 and also 360
fun getWindDirectionByDouble(windDirection: Double): WindDirections {
return when (windDirection) {
in NORTH.range -> NORTH // number between (0.0 and 45.0) OR (360.0 need to accomplish that one, now it isn't)
in NORTH_EAST.range -> NORTH_EAST // 45 and 90
(... East, South, West etc)
else -> NOT_FOUND // when angle is not between 0 and 359.9 (for now 360 is here)
Thank you in advance
EDIT:
The way to accomplish that is to make when catching my special case for 360 in function getWindDirectionByDouble() like that in bellow, but I ask, is it possible to add 360 in the range of NORTH somehow. "(0.0..45.0) and (360.0)"
fun getWindDirectionByDouble(windDirection: Double): WindDirections {
return when (windDirection) {
360.0 -> NORTH // Like that one, i will return right WindDirections.NORTH
in NORTH.range -> NORTH // number between (0.0 and 45.0) OR (360.0 need to accomplish that one, now it isn't)
in NORTH_EAST.range -> NORTH_EAST // 45 and 90
(... East, South, West etc)
else -> NOT_FOUND
There is a flaw in using a Double ClosedRange for this since it treats the start and end inclusively. You worked around that by using 45.1 for example, but then there are numbers like 45.05 that won't be in any of the ranges.
You could make your own type of range that treats the end as exclusive. And you could also design it to handle angles outside 0-360 by calculating the positive remainder (modulo).
data class AngleRange(val start: Double, val endExclusive: Double) {
operator fun contains(value: Double) =
((value % 360.0 + 360.0) % 360.0).let { it >= start && it < endExclusive }
}
Then you can fully cover the possible ranges using AngleRange(0.0, 45.0), AngleRange(45.0, 90.0), etc.
Related
Consider a 2D square tiled grid (chess board like) which contains conveyor belt like structures that can curve and move game pieces around.
I need to calculate the turn movement (TURN_LEFT, TURN_RIGHT or STAY), depending on
the direction from which a piece moves onto the field
the direction from which the underlying belt exits the field
Example:
1 2
1 |>X>|>v |
2 | | v |
The belt makes a RIGHT turn. As such, the result of calcTurn(LEFT, DOWN) should be TURN_RIGHT. Meaning the X game piece will be rotated 90° right when it moves over the curve at (1,2).
I already implemented a function but it only works on some of my test cases.
enum class Direction {
NONE,
UP,
RIGHT,
DOWN,
LEFT;
fun isOpposite(other: Direction) = this == UP && other == DOWN
|| this == DOWN && other == UP
|| this == LEFT && other == RIGHT
|| this == RIGHT && other == LEFT
}
data class Vec2(val x: Float, val y: Float)
fun Direction.toVec2() = when (this) {
Direction.NONE -> Vec2(0f, 0f)
Direction.UP -> Vec2(0f, 1f)
Direction.RIGHT -> Vec2(1f, 0f)
Direction.DOWN -> Vec2(0f, -1f)
Direction.LEFT -> Vec2(-1f, 0f)
}
fun getTurnMovement(incomingDirection: Direction, outgoingDirection: Direction): Movement {
if (incomingDirection.isOpposite(outgoingDirection) || incomingDirection == outgoingDirection) {
return Movement.STAY
}
val incVec = incomingDirection.toVec2()
val outVec = outgoingDirection.toVec2()
val angle = atan2(
incVec.x * outVec.x - incVec.y * outVec.y,
incVec.x * outVec.x + incVec.y * outVec.y
)
return when {
angle < 0 -> Movement.TURN_RIGHT
angle > 0 -> Movement.TURN_LEFT
else -> Movement.STAY
}
}
I can't quite figure out what's going wrong here, especially not because some test cases work (like DOWN+LEFT=TURN_LEFT) but others don't (like DOWN+RIGHT=STAY instead of TURN_LEFT)
You're trying to calculate the angle between two two-dimensional vectors, but are doing so incorrectly.
Mathematically, given two vectors (x1,y1) and (x2,y2), the angle between them is the angle of the second to the x-axis minus the angle of the first to the x-axis. In equation form: arctan(y2/x2) - arctan(y1/x1).
Translating that to Kotlin, you should instead use:
val angle = atan2(outVec.y, outVec.x) - atan2(incVec.y, incVec.x)
I'd note that you could achieve also your overall goal by just delineating the cases in a when statement as you only have a small number of possible directions, but perhaps you want a more general solution.
It's not answering your question of why your code isn't working, but here's another general approach you could use for wrapping ordered data like this:
enum class Direction {
UP, RIGHT, DOWN, LEFT;
companion object {
// storing thing means you only need to generate the array once
private val directions = values()
private fun getPositionWrapped(pos: Int) = directions[(pos).mod(directions.size)]
}
// using getters here as a general example
val toLeft get() = getPositionWrapped(ordinal - 1)
val toRight get() = getPositionWrapped(ordinal + 1)
val opposite get() = getPositionWrapped(ordinal + 2)
}
It's taking advantage of the fact enums are ordered, with an ordinal property to pull out the position of a particular constant. It also uses the (x).mod(y) trick where if x is negative, putting it in parentheses makes it wrap around
x| 6 5 4 3 2 1 0 -1 -2 -3 -4 -5
mod 4| 2 1 0 3 2 1 0 3 2 1 0 3
which makes it easy to grab the next or previous (or however far you want to jump) index, acting like a circular array.
Since you have a NONE value in your example (which obviously doesn't fit into this pattern) I'd probably represent that with a null Direction? instead, since it's more of a lack of a value than an actual type of direction. Depends what you're doing of course!
I would like to generate random numbers from a union of ranges in Kotlin. I know I can do something like
((1..10) + (50..100)).random()
but unfortunately this creates an intermediate list, which can be rather expensive when the ranges are large.
I know I could write a custom function to randomly select a range with a weight based on its width, followed by randomly choosing an element from that range, but I am wondering if there is a cleaner way to achieve this with Kotlin built-ins.
Suppose your ranges are nonoverlapped and sorted, if not, you could have some preprocessing to merge and sort.
This comes to an algorithm choosing:
O(1) time complexity and O(N) space complexity, where N is the total number, by expanding the range object to a set of numbers, and randomly pick one. To be compact, an array or list could be utilized as the container.
O(M) time complexity and O(1) space complexity, where M is the number of ranges, by calculating the position in a linear reduction.
O(M+log(M)) time complexity and O(M) space complexity, where M is the number of ranges, by calculating the position using a binary search. You could separate the preparation(O(M)) and generation(O(log(M))), if there are multiple generations on the same set of ranges.
For the last algorithm, imaging there's a sorted list of all available numbers, then this list can be partitioned into your ranges. So there's no need to really create this list, you just calculate the positions of your range s relative to this list. When you have a position within this list, and want to know which range it is in, do a binary search.
fun random(ranges: Array<IntRange>): Int {
// preparation
val positions = ranges.map {
it.last - it.first + 1
}.runningFold(0) { sum, item -> sum + item }
// generation
val randomPos = Random.nextInt(positions[ranges.size])
val found = positions.binarySearch(randomPos)
// binarySearch may return an "insertion point" in negative
val range = if (found < 0) -(found + 1) - 1 else found
return ranges[range].first + randomPos - positions[range]
}
Short solution
We can do it like this:
fun main() {
println(random(1..10, 50..100))
}
fun random(vararg ranges: IntRange): Int {
var index = Random.nextInt(ranges.sumOf { it.last - it.first } + ranges.size)
ranges.forEach {
val size = it.last - it.first + 1
if (index < size) {
return it.first + index
}
index -= size
}
throw IllegalStateException()
}
It uses the same approach you described, but it calls for random integer only once, not twice.
Long solution
As I said in the comment, I often miss utils in Java/Kotlin stdlib for creating collection views. If IntRange would have something like asList() and we would have a way to concatenate lists by creating a view, this would be really trivial, utilizing existing logic blocks. Views would do the trick for us, they would automatically calculate the size and translate the random number to the proper value.
I implemented a POC, maybe you will find it useful:
fun main() {
val list = listOf(1..10, 50..100).mergeAsView()
println(list.size) // 61
println(list[20]) // 60
println(list.random())
}
#JvmName("mergeIntRangesAsView")
fun Iterable<IntRange>.mergeAsView(): List<Int> = map { it.asList() }.mergeAsView()
#JvmName("mergeListsAsView")
fun <T> Iterable<List<T>>.mergeAsView(): List<T> = object : AbstractList<T>() {
override val size = this#mergeAsView.sumOf { it.size }
override fun get(index: Int): T {
if (index < 0 || index >= size) {
throw IndexOutOfBoundsException(index)
}
var remaining = index
this#mergeAsView.forEach { curr ->
if (remaining < curr.size) {
return curr[remaining]
}
remaining -= curr.size
}
throw IllegalStateException()
}
}
fun IntRange.asList(): List<Int> = object : AbstractList<Int>() {
override val size = endInclusive - start + 1
override fun get(index: Int): Int {
if (index < 0 || index >= size) {
throw IndexOutOfBoundsException(index)
}
return start + index
}
}
This code does almost exactly the same thing as short solution above. It only does this indirectly.
Once again: this is just a POC. This implementation of asList() and mergeAsView() is not at all production-ready. We should implement more methods, like for example iterator(), contains() and indexOf(), because right now they are much slower than they could be. But it should work efficiently already for your specific case. You should probably test it at least a little. Also, mergeAsView() assumes provided lists are immutable (they have fixed size) which may not be true.
It would be probably good to implement asList() for IntProgression and for other primitive types as well. Also you may prefer varargs version of mergeAsView() than extension function.
As a final note: I guess there are libraries that does this already - probably some related to immutable collections. But if you look for a relatively lightweight solution, it should work for you.
I have an array
var poses = arrayOf<Array<Double>>()
That I populate using a loop.
The output looks something like this:
poses.forEach {
println(Arrays.toString(it))
}
[-71.42510166478651, 106.43593221597114]
[104.46430594348055, 78.62761919208839]
[100.27031925094859, 79.65568893000942]
[311.2433803626159, 233.67219485640456]
[330.3015877764689, -114.9000129699181]
[34.76986782382592, -383.71914014833436]
[355.477931403836, -173.29388985868835]
[322.72821807215564, -45.99138725647516]
...
Is there an efficient way to find 10 nearest points from this list for each coordinate?
For example:
Find 10 nearest points for [-71.42510166478651, 106.43593221597114], then [104.46430594348055, 78.62761919208839] and so on.
I tried looking into numpy-like libraries for Kotlin but seeing as though I'm new to the language I couldn't figure out how to do it.
You can write a distance function with the Pythagorean theorem. (This GeeksforGeeks page might be helpful too.)
You could also use a data class for the points, instead of using an array with two double values. The code below uses the approach that Mateen Ulhaq suggested in his comment, with two modifications:
The addition of "point to" lets us create a map from a point to the ten nearest points (so we know which point the ten points are related to).
The call to ".drop(1)" before ".take(10)" keeps the point itself out of its list (since the distance to itself is 0).
This code uses a list of points, determines the nearest points and prints them for each point:
fun main() {
val poses = listOf(
Point(-71.42510166478651, 106.43593221597114),
Point(104.46430594348055, 78.62761919208839),
Point(100.27031925094859, 79.65568893000942),
Point(311.2433803626159, 233.67219485640456),
Point(330.3015877764689, -114.9000129699181),
Point(34.76986782382592, -383.71914014833436),
Point(355.477931403836, -173.29388985868835),
Point(322.72821807215564, -45.99138725647516)
)
val nearestPoints = poses.map {
point -> point to poses.sortedBy { point.distance(it) }.drop(1).take(10)
}
println("Nearest points:")
nearestPoints.forEach {
println("${it.first} is closest to ${it.second}")
}
}
data class Point(val x: Double, val y: Double) {
fun distance(that: Point): Double {
val distanceX = this.x - that.x
val distanceY = this.y - that.y
return sqrt(distanceX * distanceX + distanceY * distanceY)
}
}
If the points are evenly (or almost evenly) distributed in some area, I suggest dividing them into rectangular chunks with size area.size.x / poses.size * 10 by area.size.y / poses.size * 10.
Then to find the nearest points for any point, you only need to check neighboring chunks. Since points are evenly distributed, you can find the nearest points for all points in O(kn) where n is a number of points and k = 10.
If the points are not guaranteed to be evenly (or almost evenly) distributed, you have to divide the area into several chunks and then recursively repeat the same process for each chunk until all the sub-chunks contain at most x points. (It's hard to tell what is optimal x and optimal count of sub-chunks per chunk, you need to do some research to find it out).
Then you can find the nearest points for any point, just as you did with evenly distributed points.
A few tricks to improve performance:
Use distanceSquared instead of distance. Here is how you can implement distanceSquared:
fun Point.distanceSquared(other: Point) = (x - other.x).squared() + (y - other.y).squared()
typealias Point = Array<Double>
val Point.x get() = this[0]
val Point.y get() = this[1]
fun Double.squared() = this * this
Use PriorityQueue<Point>(10, compareBy { -it.distanceSquared(destination) }) to store nearest points, and offer(point, 10) to add points to it:
fun <E : Any> PriorityQueue<E>.offer(element: E, maxSize: Int) {
if (size < maxSize) offer(element)
else if (compare(element, peek()) > 0) {
poll()
offer(element)
}
}
// if `comparator()` returns `null` queue uses naturalOrder and `E` is `Comparable<E>`
#Suppress("UNCHECKED_CAST")
fun <E : Any> PriorityQueue<E>.compare(o1: E, o2: E) =
comparator()?.compare(o1, o2) ?: (o1 as Comparable<E>).compareTo(o2)
Divide your points into several groups and run the calculation for each group in a separate thread. It will let your program to use all available cores.
I have a double value, if that number is like this: 123.00 I need to show it as 123 only, without decimal places, but, if the number is like 123.23 or 123.2, I need to show it with the present decimal places: 123.23 or 123.2, as the case may be.
I have tried with decimal format but I couldn't find the right pattern.
It is a better way to do this than a string conversion and operate with substrings and things like that?
DecimalFormat is what you're looking for I think:
import java.text.DecimalFormat
fun main(args : Array<String>) {
val df = DecimalFormat("0.##")
println(df.format(123.0))
println(df.format(123.3))
println(df.format(123.32))
println(df.format(123.327))
}
Output:
123
123.3
123.32
123.33
Here's one way you could do it:
fun func(x: Double): String {
if (x.rem(1).compareTo(0) == 0){
return x.toInt().toString();
} else {
return x.toString();
}
}
print(func(1.32132)); //Returns 1.32132
print(func(3.00)); //Returns 3
You could use DecimalFormat with setMaximumFractionDigits. Creating an extension function would keep the complexity away from the call-site:
fun Double.toStringRounded(fracDigits: Int) = DecimalFormat().apply {
setMaximumFractionDigits(fracDigits)
}.format(this)
Usage:
3.14159.toStringRounded(2) // will be "3.14"
val listNumbers = generateSequence(1) { it + 1 }
val listNumber1to100 = listNumbers.takeWhile { it < 100 }
val secNum:Unit = listNumber1to100.forEach {it}
println(listNumber1to100.asSequence().filter { it%(listNumber1to100.forEach { it })!=0 }.toList())
I have an error in reminder sign!
This is Error: None of the following functions can be called with the arguments supplied
In your first approach, the error appears in this line:
it%(listNumber1to100.forEach { it })
A Byte, Double, Float, Int, Long or Short is prefered right after the % operator, however, forEach is a function which the return type is Unit.
In your second approach, you have the correct expression in isPrime(Int). Here are some suggestions for you:
listNumber1to100 is excluding 100 in your code, if you want to include 100 in listNumber1to100, the lambda you pass to takeWhile should be changed like this:
val listNumber1to100 = listNumbers.takeWhile { it <= 100 }
listNumber1to100.asSequence() is redundant here since listNumber1too100 is itself a TakeWhileSequence which implements Sequence.
isPrime(Int) is a bit confusing since it is check for isComposite and it does not work for every input it takes(it works for 1 to 99 only). I will rewrite it in this way:
fun isPrime(num: Int): Boolean = if (num <= 1) false else !(2..num/2).any { num % it == 0 }
Since prime number must be positive and 1 is a special case(neither a prime nor composite number), it just return false if the input is smaller or equal to 1. If not, it checks if the input is divisible by a range of number from 2 to (input/2). The range ends before (input/2) is because if it is true for num % (num/2) == 0, it is also true for num % 2 == 0, vise versa. Finally, I add a ! operator before that because a prime number should not be divisible by any of those numbers.
Finally, you can filter a list by isPrime(Int) like this:
println(listNumber1to100.filter(::isPrime).toList())
PS. It is just for reference and there must be a better implementation than this.
To answer your question about it, it represents the only lambda parameter inside a lambda expression. It is always used for function literal which has only one parameter.
The error is because the expression: listNumber1to100.forEach { it } - is not a number, it is a Unit (ref).
The compiler try to match the modulo operator to the given function signatures, e.g.: mod(Byte) / mod(Int) / mod(Long) - etc.
val listNumbers = generateSequence(1) { it + 1 }
val listNumber1to100 = listNumbers.takeWhile { it < 100 }
fun isPrime(num: Int): Boolean = listNumber1to100.asSequence().any { num%it==0 && it!=num && it!=1 }
println(listNumber1to100.asSequence().filter { !isPrime(it)}.toList())
I found this solution and worked
But why can I have a non-number here in the right side of reminder