[BAEL-3348] Moved code to algorithm-4
This commit is contained in:
parent
db85c8f275
commit
fee1da6091
|
@ -0,0 +1,93 @@
|
||||||
|
*/bin/*
|
||||||
|
bin/
|
||||||
|
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Package Files #
|
||||||
|
*.jar
|
||||||
|
*.war
|
||||||
|
*.ear
|
||||||
|
|
||||||
|
|
||||||
|
# Eclipse
|
||||||
|
.settings/
|
||||||
|
*.project
|
||||||
|
*.classpath
|
||||||
|
.prefs
|
||||||
|
*.prefs
|
||||||
|
.metadata/
|
||||||
|
|
||||||
|
# Intellij
|
||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
|
*.iws
|
||||||
|
out/
|
||||||
|
|
||||||
|
# VSCode
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# Mac
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Maven
|
||||||
|
log/*
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Gradle
|
||||||
|
.gradle/
|
||||||
|
|
||||||
|
spring-openid/src/main/resources/application.properties
|
||||||
|
.recommenders/
|
||||||
|
/spring-hibernate4/nbproject/
|
||||||
|
spring-security-openid/src/main/resources/application.properties
|
||||||
|
|
||||||
|
spring-all/*.log
|
||||||
|
|
||||||
|
SpringDataInjectionDemo/.mvn/wrapper/maven-wrapper.properties
|
||||||
|
|
||||||
|
spring-call-getters-using-reflection/.mvn/wrapper/maven-wrapper.properties
|
||||||
|
|
||||||
|
spring-check-if-a-property-is-null/.mvn/wrapper/maven-wrapper.properties
|
||||||
|
*.springBeans
|
||||||
|
|
||||||
|
20171220-JMeter.csv
|
||||||
|
|
||||||
|
.factorypath
|
||||||
|
dependency-reduced-pom.xml
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
*.dll
|
||||||
|
|
||||||
|
xml/src/test/resources/example_dom4j_new.xml
|
||||||
|
xml/src/test/resources/example_dom4j_updated.xml
|
||||||
|
xml/src/test/resources/example_jaxb_new.xml
|
||||||
|
core-java-io/hard_link.txt
|
||||||
|
core-java-io/target_link.txt
|
||||||
|
core-java/src/main/java/com/baeldung/manifest/MANIFEST.MF
|
||||||
|
ethereum/logs/
|
||||||
|
jmeter/src/main/resources/*-JMeter.csv
|
||||||
|
|
||||||
|
**/node_modules/
|
||||||
|
**/dist
|
||||||
|
**/tmp
|
||||||
|
**/out-tsc
|
||||||
|
**/nbproject/
|
||||||
|
**/nb-configuration.xml
|
||||||
|
core-scala/.cache-main
|
||||||
|
core-scala/.cache-tests
|
||||||
|
|
||||||
|
|
||||||
|
persistence-modules/hibernate5/transaction.log
|
||||||
|
apache-avro/src/main/java/com/baeldung/avro/model/
|
||||||
|
jta/transaction-logs/
|
||||||
|
software-security/sql-injection-samples/derby.log
|
||||||
|
spring-soap/src/main/java/com/baeldung/springsoap/gen/
|
||||||
|
/report-*.json
|
||||||
|
transaction.log
|
||||||
|
*-shell.log
|
||||||
|
|
||||||
|
apache-cxf/cxf-aegis/baeldung.xml
|
||||||
|
apache-fop/src/test/resources/input.xml
|
||||||
|
apache-fop/src/test/resources/output_herold.pdf
|
||||||
|
apache-fop/src/test/resources/output_html2fo.pdf
|
||||||
|
apache-fop/src/test/resources/output_jtidy.pdf
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2017 Eugen Paraschiv
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,54 @@
|
||||||
|
|
||||||
|
The Courses
|
||||||
|
==============================
|
||||||
|
|
||||||
|
|
||||||
|
Here's the new "Learn Spring" course: <br/>
|
||||||
|
**[>> LEARN SPRING - THE MASTER CLASS](https://www.baeldung.com/learn-spring-course?utm_source=github&utm_medium=social&utm_content=tutorials&utm_campaign=ls#master-class)**
|
||||||
|
|
||||||
|
Here's the Master Class of "REST With Spring" (along with the new announced Boot 2 material): <br/>
|
||||||
|
**[>> THE REST WITH SPRING - MASTER CLASS](https://www.baeldung.com/rest-with-spring-course?utm_source=github&utm_medium=social&utm_content=tutorials&utm_campaign=rws#master-class)**
|
||||||
|
|
||||||
|
And here's the Master Class of "Learn Spring Security": <br/>
|
||||||
|
**[>> LEARN SPRING SECURITY - MASTER CLASS](https://www.baeldung.com/learn-spring-security-course?utm_source=github&utm_medium=social&utm_content=tutorials&utm_campaign=lss#master-class)**
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Java and Spring Tutorials
|
||||||
|
================
|
||||||
|
|
||||||
|
This project is **a collection of small and focused tutorials** - each covering a single and well defined area of development in the Java ecosystem.
|
||||||
|
A strong focus of these is, of course, the Spring Framework - Spring, Spring Boot and Spring Security.
|
||||||
|
In additional to Spring, the modules here are covering a number of aspects in Java.
|
||||||
|
|
||||||
|
|
||||||
|
Building the project
|
||||||
|
====================
|
||||||
|
To do the full build, do: `mvn clean install`
|
||||||
|
|
||||||
|
|
||||||
|
Building a single module
|
||||||
|
====================
|
||||||
|
To build a specific module run the command: `mvn clean install` in the module directory
|
||||||
|
|
||||||
|
|
||||||
|
Running a Spring Boot module
|
||||||
|
====================
|
||||||
|
To run a Spring Boot module run the command: `mvn spring-boot:run` in the module directory
|
||||||
|
|
||||||
|
|
||||||
|
Working with the IDE
|
||||||
|
====================
|
||||||
|
This repo contains a large number of modules.
|
||||||
|
When you're working with an individual module, there's no need to import all of them (or build all of them) - you can simply import that particular module in either Eclipse or IntelliJ.
|
||||||
|
|
||||||
|
|
||||||
|
Running Tests
|
||||||
|
=============
|
||||||
|
The command `mvn clean install` will run the unit tests in a module.
|
||||||
|
To run the integration tests, use the command `mvn clean install -Pintegration-lite-first`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
## Akka HTTP
|
||||||
|
|
||||||
|
This module contains articles about Akka HTTP.
|
||||||
|
|
||||||
|
### Relevant articles:
|
||||||
|
|
||||||
|
- [Introduction to Akka HTTP](https://www.baeldung.com/akka-http)
|
|
@ -0,0 +1,47 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<project
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>akka-http</artifactId>
|
||||||
|
<name>akka-http</name>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<artifactId>parent-modules</artifactId>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.typesafe.akka</groupId>
|
||||||
|
<artifactId>akka-http_2.12</artifactId>
|
||||||
|
<version>${akka.http.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.typesafe.akka</groupId>
|
||||||
|
<artifactId>akka-stream_2.12</artifactId>
|
||||||
|
<version>${akka.stream.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.typesafe.akka</groupId>
|
||||||
|
<artifactId>akka-http-jackson_2.12</artifactId>
|
||||||
|
<version>${akka.http.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.typesafe.akka</groupId>
|
||||||
|
<artifactId>akka-http-testkit_2.12</artifactId>
|
||||||
|
<version>${akka.http.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
|
<akka.http.version>10.0.11</akka.http.version>
|
||||||
|
<akka.stream.version>2.5.11</akka.stream.version>
|
||||||
|
</properties>
|
||||||
|
</project>
|
|
@ -0,0 +1,26 @@
|
||||||
|
package com.baeldung.akkahttp;
|
||||||
|
|
||||||
|
public class User {
|
||||||
|
|
||||||
|
private final Long id;
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
public User() {
|
||||||
|
this.name = "";
|
||||||
|
this.id = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User(Long id, String name) {
|
||||||
|
this.name = name;
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
package com.baeldung.akkahttp;
|
||||||
|
|
||||||
|
import akka.actor.AbstractActor;
|
||||||
|
import akka.actor.Props;
|
||||||
|
import akka.japi.pf.FI;
|
||||||
|
import com.baeldung.akkahttp.UserMessages.ActionPerformed;
|
||||||
|
import com.baeldung.akkahttp.UserMessages.CreateUserMessage;
|
||||||
|
import com.baeldung.akkahttp.UserMessages.GetUserMessage;
|
||||||
|
|
||||||
|
|
||||||
|
class UserActor extends AbstractActor {
|
||||||
|
|
||||||
|
private UserService userService = new UserService();
|
||||||
|
|
||||||
|
static Props props() {
|
||||||
|
return Props.create(UserActor.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Receive createReceive() {
|
||||||
|
return receiveBuilder()
|
||||||
|
.match(CreateUserMessage.class, handleCreateUser())
|
||||||
|
.match(GetUserMessage.class, handleGetUser())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private FI.UnitApply<CreateUserMessage> handleCreateUser() {
|
||||||
|
return createUserMessageMessage -> {
|
||||||
|
userService.createUser(createUserMessageMessage.getUser());
|
||||||
|
sender().tell(new ActionPerformed(String.format("User %s created.", createUserMessageMessage.getUser()
|
||||||
|
.getName())), getSelf());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private FI.UnitApply<GetUserMessage> handleGetUser() {
|
||||||
|
return getUserMessageMessage -> {
|
||||||
|
sender().tell(userService.getUser(getUserMessageMessage.getUserId()), getSelf());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package com.baeldung.akkahttp;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public interface UserMessages {
|
||||||
|
|
||||||
|
class ActionPerformed implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private final String description;
|
||||||
|
|
||||||
|
public ActionPerformed(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CreateUserMessage implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
private final User user;
|
||||||
|
|
||||||
|
public CreateUserMessage(User user) {
|
||||||
|
this.user = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GetUserMessage implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
private final Long userId;
|
||||||
|
|
||||||
|
public GetUserMessage(Long userId) {
|
||||||
|
this.userId = userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getUserId() {
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
package com.baeldung.akkahttp;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.CompletionStage;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import akka.actor.ActorRef;
|
||||||
|
import akka.actor.ActorSystem;
|
||||||
|
import akka.http.javadsl.marshallers.jackson.Jackson;
|
||||||
|
import akka.http.javadsl.model.StatusCodes;
|
||||||
|
import akka.http.javadsl.server.HttpApp;
|
||||||
|
import akka.http.javadsl.server.Route;
|
||||||
|
import akka.pattern.PatternsCS;
|
||||||
|
import akka.util.Timeout;
|
||||||
|
import com.baeldung.akkahttp.UserMessages.ActionPerformed;
|
||||||
|
import com.baeldung.akkahttp.UserMessages.CreateUserMessage;
|
||||||
|
import com.baeldung.akkahttp.UserMessages.GetUserMessage;
|
||||||
|
import scala.concurrent.duration.Duration;
|
||||||
|
import static akka.http.javadsl.server.PathMatchers.*;
|
||||||
|
|
||||||
|
class UserServer extends HttpApp {
|
||||||
|
|
||||||
|
private final ActorRef userActor;
|
||||||
|
|
||||||
|
Timeout timeout = new Timeout(Duration.create(5, TimeUnit.SECONDS));
|
||||||
|
|
||||||
|
UserServer(ActorRef userActor) {
|
||||||
|
this.userActor = userActor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Route routes() {
|
||||||
|
return path("users", this::postUser)
|
||||||
|
.orElse(path(segment("users").slash(longSegment()), id ->
|
||||||
|
route(getUser(id))));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Route getUser(Long id) {
|
||||||
|
return get(() -> {
|
||||||
|
CompletionStage<Optional<User>> user = PatternsCS.ask(userActor, new GetUserMessage(id), timeout)
|
||||||
|
.thenApply(obj -> (Optional<User>) obj);
|
||||||
|
|
||||||
|
return onSuccess(() -> user, performed -> {
|
||||||
|
if (performed.isPresent())
|
||||||
|
return complete(StatusCodes.OK, performed.get(), Jackson.marshaller());
|
||||||
|
else
|
||||||
|
return complete(StatusCodes.NOT_FOUND);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Route postUser() {
|
||||||
|
return route(post(() -> entity(Jackson.unmarshaller(User.class), user -> {
|
||||||
|
CompletionStage<ActionPerformed> userCreated = PatternsCS.ask(userActor, new CreateUserMessage(user), timeout)
|
||||||
|
.thenApply(obj -> (ActionPerformed) obj);
|
||||||
|
|
||||||
|
return onSuccess(() -> userCreated, performed -> {
|
||||||
|
return complete(StatusCodes.CREATED, performed, Jackson.marshaller());
|
||||||
|
});
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
ActorSystem system = ActorSystem.create("userServer");
|
||||||
|
ActorRef userActor = system.actorOf(UserActor.props(), "userActor");
|
||||||
|
UserServer server = new UserServer(userActor);
|
||||||
|
server.startServer("localhost", 8080, system);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package com.baeldung.akkahttp;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class UserService {
|
||||||
|
|
||||||
|
private final static List<User> users = new ArrayList<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
users.add(new User(1l, "Alice"));
|
||||||
|
users.add(new User(2l, "Bob"));
|
||||||
|
users.add(new User(3l, "Chris"));
|
||||||
|
users.add(new User(4l, "Dick"));
|
||||||
|
users.add(new User(5l, "Eve"));
|
||||||
|
users.add(new User(6l, "Finn"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<User> getUser(Long id) {
|
||||||
|
return users.stream()
|
||||||
|
.filter(user -> user.getId()
|
||||||
|
.equals(id))
|
||||||
|
.findFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createUser(User user) {
|
||||||
|
users.add(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<User> getUsers(){
|
||||||
|
return users;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package com.baeldung.akkahttp;
|
||||||
|
|
||||||
|
import akka.actor.ActorRef;
|
||||||
|
import akka.actor.ActorSystem;
|
||||||
|
import akka.http.javadsl.model.ContentTypes;
|
||||||
|
import akka.http.javadsl.model.HttpEntities;
|
||||||
|
import akka.http.javadsl.model.HttpRequest;
|
||||||
|
import akka.http.javadsl.testkit.JUnitRouteTest;
|
||||||
|
import akka.http.javadsl.testkit.TestRoute;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class UserServerUnitTest extends JUnitRouteTest {
|
||||||
|
|
||||||
|
ActorSystem system = ActorSystem.create("helloAkkaHttpServer");
|
||||||
|
|
||||||
|
ActorRef userActorRef = system.actorOf(UserActor.props(), "userActor");
|
||||||
|
|
||||||
|
TestRoute appRoute = testRoute(new UserServer(userActorRef).routes());
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenRequest_thenActorResponds() {
|
||||||
|
|
||||||
|
appRoute.run(HttpRequest.GET("/users/1"))
|
||||||
|
.assertEntity(alice())
|
||||||
|
.assertStatusCode(200);
|
||||||
|
|
||||||
|
appRoute.run(HttpRequest.GET("/users/42"))
|
||||||
|
.assertStatusCode(404);
|
||||||
|
|
||||||
|
appRoute.run(HttpRequest.DELETE("/users/1"))
|
||||||
|
.assertStatusCode(200);
|
||||||
|
|
||||||
|
appRoute.run(HttpRequest.DELETE("/users/42"))
|
||||||
|
.assertStatusCode(200);
|
||||||
|
|
||||||
|
appRoute.run(HttpRequest.POST("/users")
|
||||||
|
.withEntity(HttpEntities.create(ContentTypes.APPLICATION_JSON, zaphod())))
|
||||||
|
.assertStatusCode(201);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private String alice() {
|
||||||
|
return "{\"id\":1,\"name\":\"Alice\"}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String zaphod() {
|
||||||
|
return "{\"id\":42,\"name\":\"Zaphod\"}";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
## Akka Streams
|
||||||
|
|
||||||
|
This module contains articles about Akka Streams.
|
||||||
|
|
||||||
|
### Relevant articles
|
||||||
|
|
||||||
|
- [Guide to Akka Streams](https://www.baeldung.com/akka-streams)
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>akka-streams</artifactId>
|
||||||
|
<name>akka-streams</name>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<artifactId>parent-modules</artifactId>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.typesafe.akka</groupId>
|
||||||
|
<artifactId>akka-stream_${scala.version}</artifactId>
|
||||||
|
<version>${akkastreams.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.typesafe.akka</groupId>
|
||||||
|
<artifactId>akka-stream-testkit_${scala.version}</artifactId>
|
||||||
|
<version>${akkastreams.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<akkastreams.version>2.5.2</akkastreams.version>
|
||||||
|
<scala.version>2.11</scala.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,14 @@
|
||||||
|
package com.baeldung.akkastreams;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.CompletionStage;
|
||||||
|
|
||||||
|
public class AverageRepository {
|
||||||
|
CompletionStage<Double> save(Double average) {
|
||||||
|
return CompletableFuture.supplyAsync(() -> {
|
||||||
|
System.out.println("saving average: " + average);
|
||||||
|
return average;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
package com.baeldung.akkastreams;
|
||||||
|
|
||||||
|
|
||||||
|
import akka.Done;
|
||||||
|
import akka.NotUsed;
|
||||||
|
import akka.actor.ActorSystem;
|
||||||
|
import akka.stream.ActorMaterializer;
|
||||||
|
import akka.stream.javadsl.Flow;
|
||||||
|
import akka.stream.javadsl.Keep;
|
||||||
|
import akka.stream.javadsl.Sink;
|
||||||
|
import akka.stream.javadsl.Source;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.CompletionStage;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class DataImporter {
|
||||||
|
private final ActorSystem actorSystem;
|
||||||
|
private final AverageRepository averageRepository = new AverageRepository();
|
||||||
|
|
||||||
|
public DataImporter(ActorSystem actorSystem) {
|
||||||
|
this.actorSystem = actorSystem;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Integer> parseLine(String line) {
|
||||||
|
String[] fields = line.split(";");
|
||||||
|
return Arrays.stream(fields)
|
||||||
|
.map(Integer::parseInt)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Flow<String, Integer, NotUsed> parseContent() {
|
||||||
|
return Flow.of(String.class).mapConcat(this::parseLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Flow<Integer, Double, NotUsed> computeAverage() {
|
||||||
|
return Flow.of(Integer.class).grouped(2).mapAsyncUnordered(8, integers ->
|
||||||
|
CompletableFuture.supplyAsync(() -> integers
|
||||||
|
.stream()
|
||||||
|
.mapToDouble(v -> v)
|
||||||
|
.average()
|
||||||
|
.orElse(-1.0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Flow<String, Double, NotUsed> calculateAverage() {
|
||||||
|
return Flow.of(String.class)
|
||||||
|
.via(parseContent())
|
||||||
|
.via(computeAverage());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Sink<Double, CompletionStage<Done>> storeAverages() {
|
||||||
|
return Flow.of(Double.class)
|
||||||
|
.mapAsyncUnordered(4, averageRepository::save)
|
||||||
|
.toMat(Sink.ignore(), Keep.right());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CompletionStage<Done> calculateAverageForContent(String content) {
|
||||||
|
return Source.single(content)
|
||||||
|
.via(calculateAverage())
|
||||||
|
.runWith(storeAverages(), ActorMaterializer.create(actorSystem))
|
||||||
|
.whenComplete((d, e) -> {
|
||||||
|
if (d != null) {
|
||||||
|
System.out.println("Import finished ");
|
||||||
|
} else {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<configuration>
|
||||||
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<encoder>
|
||||||
|
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
|
||||||
|
</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<root level="INFO">
|
||||||
|
<appender-ref ref="STDOUT" />
|
||||||
|
</root>
|
||||||
|
</configuration>
|
|
@ -0,0 +1,43 @@
|
||||||
|
package com.baeldung.akkastreams;
|
||||||
|
|
||||||
|
import akka.NotUsed;
|
||||||
|
import akka.actor.ActorSystem;
|
||||||
|
import akka.stream.ActorMaterializer;
|
||||||
|
import akka.stream.javadsl.Flow;
|
||||||
|
import akka.stream.javadsl.Source;
|
||||||
|
import akka.stream.testkit.javadsl.TestSink;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
|
||||||
|
public class DataImporterUnitTest {
|
||||||
|
private final ActorSystem actorSystem = ActorSystem.create();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenStreamOfIntegers_whenCalculateAverageOfPairs_thenShouldReturnProperResults() {
|
||||||
|
//given
|
||||||
|
Flow<String, Double, NotUsed> tested = new DataImporter(actorSystem).calculateAverage();
|
||||||
|
String input = "1;9;11;0";
|
||||||
|
|
||||||
|
//when
|
||||||
|
Source<Double, NotUsed> flow = Source.single(input).via(tested);
|
||||||
|
|
||||||
|
//then
|
||||||
|
flow
|
||||||
|
.runWith(TestSink.probe(actorSystem), ActorMaterializer.create(actorSystem))
|
||||||
|
.request(4)
|
||||||
|
.expectNextUnordered(5d, 5.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenStreamOfIntegers_whenCalculateAverageAndSaveToSink_thenShouldFinishSuccessfully() {
|
||||||
|
//given
|
||||||
|
DataImporter dataImporter = new DataImporter(actorSystem);
|
||||||
|
String input = "10;90;110;10";
|
||||||
|
|
||||||
|
//when
|
||||||
|
dataImporter.calculateAverageForContent(input)
|
||||||
|
.thenAccept(d -> actorSystem.terminate());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
/target/
|
||||||
|
.settings/
|
||||||
|
.classpath
|
||||||
|
.project
|
|
@ -0,0 +1,10 @@
|
||||||
|
## Genetic Algorithms
|
||||||
|
|
||||||
|
This module contains articles about genetic algorithms.
|
||||||
|
|
||||||
|
### Relevant articles:
|
||||||
|
|
||||||
|
- [Introduction to Jenetics Library](https://www.baeldung.com/jenetics)
|
||||||
|
- [Ant Colony Optimization](https://www.baeldung.com/java-ant-colony-optimization)
|
||||||
|
- [Design a Genetic Algorithm in Java](https://www.baeldung.com/java-genetic-algorithm)
|
||||||
|
- [The Traveling Salesman Problem in Java](https://www.baeldung.com/java-simulated-annealing-for-traveling-salesman)
|
|
@ -0,0 +1,64 @@
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>algorithms-genetic</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<name>algorithms-genetic</name>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<artifactId>parent-modules</artifactId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-math3</artifactId>
|
||||||
|
<version>${commons-math3.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-codec</groupId>
|
||||||
|
<artifactId>commons-codec</artifactId>
|
||||||
|
<version>${commons-codec.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>${lombok.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.jenetics</groupId>
|
||||||
|
<artifactId>jenetics</artifactId>
|
||||||
|
<version>${io.jenetics.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.assertj</groupId>
|
||||||
|
<artifactId>assertj-core</artifactId>
|
||||||
|
<version>${org.assertj.core.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<pluginManagement>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
|
<artifactId>exec-maven-plugin</artifactId>
|
||||||
|
<version>${exec-maven-plugin.version}</version>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</pluginManagement>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<commons-math3.version>3.6.1</commons-math3.version>
|
||||||
|
<io.jenetics.version>3.7.0</io.jenetics.version>
|
||||||
|
<org.assertj.core.version>3.9.0</org.assertj.core.version>
|
||||||
|
<commons-codec.version>1.11</commons-codec.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,38 @@
|
||||||
|
package com.baeldung.algorithms;
|
||||||
|
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
|
import com.baeldung.algorithms.ga.annealing.SimulatedAnnealing;
|
||||||
|
import com.baeldung.algorithms.ga.ant_colony.AntColonyOptimization;
|
||||||
|
import com.baeldung.algorithms.ga.binary.SimpleGeneticAlgorithm;
|
||||||
|
|
||||||
|
public class RunAlgorithm {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
|
||||||
|
Scanner in = new Scanner(System.in);
|
||||||
|
System.out.println("Run algorithm:");
|
||||||
|
System.out.println("1 - Simulated Annealing");
|
||||||
|
System.out.println("2 - Simple Genetic Algorithm");
|
||||||
|
System.out.println("3 - Ant Colony");
|
||||||
|
int decision = in.nextInt();
|
||||||
|
switch (decision) {
|
||||||
|
case 1:
|
||||||
|
System.out.println(
|
||||||
|
"Optimized distance for travel: " + SimulatedAnnealing.simulateAnnealing(10, 10000, 0.9995));
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
SimpleGeneticAlgorithm ga = new SimpleGeneticAlgorithm();
|
||||||
|
ga.runAlgorithm(50, "1011000100000100010000100000100111001000000100000100000000001111");
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
AntColonyOptimization antColony = new AntColonyOptimization(21);
|
||||||
|
antColony.startAntOptimization();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
System.out.println("Unknown option");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
in.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.baeldung.algorithms.ga.annealing;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class City {
|
||||||
|
|
||||||
|
private int x;
|
||||||
|
private int y;
|
||||||
|
|
||||||
|
public City() {
|
||||||
|
this.x = (int) (Math.random() * 500);
|
||||||
|
this.y = (int) (Math.random() * 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double distanceToCity(City city) {
|
||||||
|
int x = Math.abs(getX() - city.getX());
|
||||||
|
int y = Math.abs(getY() - city.getY());
|
||||||
|
return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package com.baeldung.algorithms.ga.annealing;
|
||||||
|
|
||||||
|
public class SimulatedAnnealing {
|
||||||
|
|
||||||
|
private static Travel travel = new Travel(10);
|
||||||
|
|
||||||
|
public static double simulateAnnealing(double startingTemperature, int numberOfIterations, double coolingRate) {
|
||||||
|
System.out.println("Starting SA with temperature: " + startingTemperature + ", # of iterations: " + numberOfIterations + " and colling rate: " + coolingRate);
|
||||||
|
double t = startingTemperature;
|
||||||
|
travel.generateInitialTravel();
|
||||||
|
double bestDistance = travel.getDistance();
|
||||||
|
System.out.println("Initial distance of travel: " + bestDistance);
|
||||||
|
Travel bestSolution = travel;
|
||||||
|
Travel currentSolution = bestSolution;
|
||||||
|
|
||||||
|
for (int i = 0; i < numberOfIterations; i++) {
|
||||||
|
if (t > 0.1) {
|
||||||
|
currentSolution.swapCities();
|
||||||
|
double currentDistance = currentSolution.getDistance();
|
||||||
|
if (currentDistance < bestDistance) {
|
||||||
|
bestDistance = currentDistance;
|
||||||
|
} else if (Math.exp((bestDistance - currentDistance) / t) < Math.random()) {
|
||||||
|
currentSolution.revertSwap();
|
||||||
|
}
|
||||||
|
t *= coolingRate;
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (i % 100 == 0) {
|
||||||
|
System.out.println("Iteration #" + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bestDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
package com.baeldung.algorithms.ga.annealing;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class Travel {
|
||||||
|
|
||||||
|
private ArrayList<City> travel = new ArrayList<>();
|
||||||
|
private ArrayList<City> previousTravel = new ArrayList<>();
|
||||||
|
|
||||||
|
public Travel(int numberOfCities) {
|
||||||
|
for (int i = 0; i < numberOfCities; i++) {
|
||||||
|
travel.add(new City());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void generateInitialTravel() {
|
||||||
|
if (travel.isEmpty())
|
||||||
|
new Travel(10);
|
||||||
|
Collections.shuffle(travel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void swapCities() {
|
||||||
|
int a = generateRandomIndex();
|
||||||
|
int b = generateRandomIndex();
|
||||||
|
previousTravel = travel;
|
||||||
|
City x = travel.get(a);
|
||||||
|
City y = travel.get(b);
|
||||||
|
travel.set(a, y);
|
||||||
|
travel.set(b, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void revertSwap() {
|
||||||
|
travel = previousTravel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int generateRandomIndex() {
|
||||||
|
return (int) (Math.random() * travel.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
public City getCity(int index) {
|
||||||
|
return travel.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDistance() {
|
||||||
|
int distance = 0;
|
||||||
|
for (int index = 0; index < travel.size(); index++) {
|
||||||
|
City starting = getCity(index);
|
||||||
|
City destination;
|
||||||
|
if (index + 1 < travel.size()) {
|
||||||
|
destination = getCity(index + 1);
|
||||||
|
} else {
|
||||||
|
destination = getCity(0);
|
||||||
|
}
|
||||||
|
distance += starting.distanceToCity(destination);
|
||||||
|
}
|
||||||
|
return distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package com.baeldung.algorithms.ga.ant_colony;
|
||||||
|
|
||||||
|
public class Ant {
|
||||||
|
|
||||||
|
protected int trailSize;
|
||||||
|
protected int trail[];
|
||||||
|
protected boolean visited[];
|
||||||
|
|
||||||
|
public Ant(int tourSize) {
|
||||||
|
this.trailSize = tourSize;
|
||||||
|
this.trail = new int[tourSize];
|
||||||
|
this.visited = new boolean[tourSize];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void visitCity(int currentIndex, int city) {
|
||||||
|
trail[currentIndex + 1] = city;
|
||||||
|
visited[city] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean visited(int i) {
|
||||||
|
return visited[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected double trailLength(double graph[][]) {
|
||||||
|
double length = graph[trail[trailSize - 1]][trail[0]];
|
||||||
|
for (int i = 0; i < trailSize - 1; i++) {
|
||||||
|
length += graph[trail[i]][trail[i + 1]];
|
||||||
|
}
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void clear() {
|
||||||
|
for (int i = 0; i < trailSize; i++)
|
||||||
|
visited[i] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,203 @@
|
||||||
|
package com.baeldung.algorithms.ga.ant_colony;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.OptionalInt;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
|
public class AntColonyOptimization {
|
||||||
|
|
||||||
|
private double c = 1.0;
|
||||||
|
private double alpha = 1;
|
||||||
|
private double beta = 5;
|
||||||
|
private double evaporation = 0.5;
|
||||||
|
private double Q = 500;
|
||||||
|
private double antFactor = 0.8;
|
||||||
|
private double randomFactor = 0.01;
|
||||||
|
|
||||||
|
private int maxIterations = 1000;
|
||||||
|
|
||||||
|
private int numberOfCities;
|
||||||
|
private int numberOfAnts;
|
||||||
|
private double graph[][];
|
||||||
|
private double trails[][];
|
||||||
|
private List<Ant> ants = new ArrayList<>();
|
||||||
|
private Random random = new Random();
|
||||||
|
private double probabilities[];
|
||||||
|
|
||||||
|
private int currentIndex;
|
||||||
|
|
||||||
|
private int[] bestTourOrder;
|
||||||
|
private double bestTourLength;
|
||||||
|
|
||||||
|
public AntColonyOptimization(int noOfCities) {
|
||||||
|
graph = generateRandomMatrix(noOfCities);
|
||||||
|
numberOfCities = graph.length;
|
||||||
|
numberOfAnts = (int) (numberOfCities * antFactor);
|
||||||
|
|
||||||
|
trails = new double[numberOfCities][numberOfCities];
|
||||||
|
probabilities = new double[numberOfCities];
|
||||||
|
IntStream.range(0, numberOfAnts)
|
||||||
|
.forEach(i -> ants.add(new Ant(numberOfCities)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate initial solution
|
||||||
|
*/
|
||||||
|
public double[][] generateRandomMatrix(int n) {
|
||||||
|
double[][] randomMatrix = new double[n][n];
|
||||||
|
IntStream.range(0, n)
|
||||||
|
.forEach(i -> IntStream.range(0, n)
|
||||||
|
.forEach(j -> randomMatrix[i][j] = Math.abs(random.nextInt(100) + 1)));
|
||||||
|
return randomMatrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform ant optimization
|
||||||
|
*/
|
||||||
|
public void startAntOptimization() {
|
||||||
|
IntStream.rangeClosed(1, 3)
|
||||||
|
.forEach(i -> {
|
||||||
|
System.out.println("Attempt #" + i);
|
||||||
|
solve();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this method to run the main logic
|
||||||
|
*/
|
||||||
|
public int[] solve() {
|
||||||
|
setupAnts();
|
||||||
|
clearTrails();
|
||||||
|
IntStream.range(0, maxIterations)
|
||||||
|
.forEach(i -> {
|
||||||
|
moveAnts();
|
||||||
|
updateTrails();
|
||||||
|
updateBest();
|
||||||
|
});
|
||||||
|
System.out.println("Best tour length: " + (bestTourLength - numberOfCities));
|
||||||
|
System.out.println("Best tour order: " + Arrays.toString(bestTourOrder));
|
||||||
|
return bestTourOrder.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare ants for the simulation
|
||||||
|
*/
|
||||||
|
private void setupAnts() {
|
||||||
|
IntStream.range(0, numberOfAnts)
|
||||||
|
.forEach(i -> {
|
||||||
|
ants.forEach(ant -> {
|
||||||
|
ant.clear();
|
||||||
|
ant.visitCity(-1, random.nextInt(numberOfCities));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
currentIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* At each iteration, move ants
|
||||||
|
*/
|
||||||
|
private void moveAnts() {
|
||||||
|
IntStream.range(currentIndex, numberOfCities - 1)
|
||||||
|
.forEach(i -> {
|
||||||
|
ants.forEach(ant -> ant.visitCity(currentIndex, selectNextCity(ant)));
|
||||||
|
currentIndex++;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select next city for each ant
|
||||||
|
*/
|
||||||
|
private int selectNextCity(Ant ant) {
|
||||||
|
int t = random.nextInt(numberOfCities - currentIndex);
|
||||||
|
if (random.nextDouble() < randomFactor) {
|
||||||
|
OptionalInt cityIndex = IntStream.range(0, numberOfCities)
|
||||||
|
.filter(i -> i == t && !ant.visited(i))
|
||||||
|
.findFirst();
|
||||||
|
if (cityIndex.isPresent()) {
|
||||||
|
return cityIndex.getAsInt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
calculateProbabilities(ant);
|
||||||
|
double r = random.nextDouble();
|
||||||
|
double total = 0;
|
||||||
|
for (int i = 0; i < numberOfCities; i++) {
|
||||||
|
total += probabilities[i];
|
||||||
|
if (total >= r) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new RuntimeException("There are no other cities");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the next city picks probabilites
|
||||||
|
*/
|
||||||
|
public void calculateProbabilities(Ant ant) {
|
||||||
|
int i = ant.trail[currentIndex];
|
||||||
|
double pheromone = 0.0;
|
||||||
|
for (int l = 0; l < numberOfCities; l++) {
|
||||||
|
if (!ant.visited(l)) {
|
||||||
|
pheromone += Math.pow(trails[i][l], alpha) * Math.pow(1.0 / graph[i][l], beta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int j = 0; j < numberOfCities; j++) {
|
||||||
|
if (ant.visited(j)) {
|
||||||
|
probabilities[j] = 0.0;
|
||||||
|
} else {
|
||||||
|
double numerator = Math.pow(trails[i][j], alpha) * Math.pow(1.0 / graph[i][j], beta);
|
||||||
|
probabilities[j] = numerator / pheromone;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update trails that ants used
|
||||||
|
*/
|
||||||
|
private void updateTrails() {
|
||||||
|
for (int i = 0; i < numberOfCities; i++) {
|
||||||
|
for (int j = 0; j < numberOfCities; j++) {
|
||||||
|
trails[i][j] *= evaporation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Ant a : ants) {
|
||||||
|
double contribution = Q / a.trailLength(graph);
|
||||||
|
for (int i = 0; i < numberOfCities - 1; i++) {
|
||||||
|
trails[a.trail[i]][a.trail[i + 1]] += contribution;
|
||||||
|
}
|
||||||
|
trails[a.trail[numberOfCities - 1]][a.trail[0]] += contribution;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the best solution
|
||||||
|
*/
|
||||||
|
private void updateBest() {
|
||||||
|
if (bestTourOrder == null) {
|
||||||
|
bestTourOrder = ants.get(0).trail;
|
||||||
|
bestTourLength = ants.get(0)
|
||||||
|
.trailLength(graph);
|
||||||
|
}
|
||||||
|
for (Ant a : ants) {
|
||||||
|
if (a.trailLength(graph) < bestTourLength) {
|
||||||
|
bestTourLength = a.trailLength(graph);
|
||||||
|
bestTourOrder = a.trail.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear trails after simulation
|
||||||
|
*/
|
||||||
|
private void clearTrails() {
|
||||||
|
IntStream.range(0, numberOfCities)
|
||||||
|
.forEach(i -> {
|
||||||
|
IntStream.range(0, numberOfCities)
|
||||||
|
.forEach(j -> trails[i][j] = c);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
package com.baeldung.algorithms.ga.binary;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class Individual {
|
||||||
|
|
||||||
|
protected int defaultGeneLength = 64;
|
||||||
|
private byte[] genes = new byte[defaultGeneLength];
|
||||||
|
private int fitness = 0;
|
||||||
|
|
||||||
|
public Individual() {
|
||||||
|
for (int i = 0; i < genes.length; i++) {
|
||||||
|
byte gene = (byte) Math.round(Math.random());
|
||||||
|
genes[i] = gene;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected byte getSingleGene(int index) {
|
||||||
|
return genes[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setSingleGene(int index, byte value) {
|
||||||
|
genes[index] = value;
|
||||||
|
fitness = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getFitness() {
|
||||||
|
if (fitness == 0) {
|
||||||
|
fitness = SimpleGeneticAlgorithm.getFitness(this);
|
||||||
|
}
|
||||||
|
return fitness;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
String geneString = "";
|
||||||
|
for (int i = 0; i < genes.length; i++) {
|
||||||
|
geneString += getSingleGene(i);
|
||||||
|
}
|
||||||
|
return geneString;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package com.baeldung.algorithms.ga.binary;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class Population {
|
||||||
|
|
||||||
|
private List<Individual> individuals;
|
||||||
|
|
||||||
|
public Population(int size, boolean createNew) {
|
||||||
|
individuals = new ArrayList<>();
|
||||||
|
if (createNew) {
|
||||||
|
createNewPopulation(size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Individual getIndividual(int index) {
|
||||||
|
return individuals.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Individual getFittest() {
|
||||||
|
Individual fittest = individuals.get(0);
|
||||||
|
for (int i = 0; i < individuals.size(); i++) {
|
||||||
|
if (fittest.getFitness() <= getIndividual(i).getFitness()) {
|
||||||
|
fittest = getIndividual(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fittest;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createNewPopulation(int size) {
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
Individual newIndividual = new Individual();
|
||||||
|
individuals.add(i, newIndividual);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
package com.baeldung.algorithms.ga.binary;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class SimpleGeneticAlgorithm {
|
||||||
|
|
||||||
|
private static final double uniformRate = 0.5;
|
||||||
|
private static final double mutationRate = 0.025;
|
||||||
|
private static final int tournamentSize = 5;
|
||||||
|
private static final boolean elitism = true;
|
||||||
|
private static byte[] solution = new byte[64];
|
||||||
|
|
||||||
|
public boolean runAlgorithm(int populationSize, String solution) {
|
||||||
|
if (solution.length() != SimpleGeneticAlgorithm.solution.length) {
|
||||||
|
throw new RuntimeException("The solution needs to have " + SimpleGeneticAlgorithm.solution.length + " bytes");
|
||||||
|
}
|
||||||
|
setSolution(solution);
|
||||||
|
Population myPop = new Population(populationSize, true);
|
||||||
|
|
||||||
|
int generationCount = 1;
|
||||||
|
while (myPop.getFittest().getFitness() < getMaxFitness()) {
|
||||||
|
System.out.println("Generation: " + generationCount + " Correct genes found: " + myPop.getFittest().getFitness());
|
||||||
|
myPop = evolvePopulation(myPop);
|
||||||
|
generationCount++;
|
||||||
|
}
|
||||||
|
System.out.println("Solution found!");
|
||||||
|
System.out.println("Generation: " + generationCount);
|
||||||
|
System.out.println("Genes: ");
|
||||||
|
System.out.println(myPop.getFittest());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Population evolvePopulation(Population pop) {
|
||||||
|
int elitismOffset;
|
||||||
|
Population newPopulation = new Population(pop.getIndividuals().size(), false);
|
||||||
|
|
||||||
|
if (elitism) {
|
||||||
|
newPopulation.getIndividuals().add(0, pop.getFittest());
|
||||||
|
elitismOffset = 1;
|
||||||
|
} else {
|
||||||
|
elitismOffset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = elitismOffset; i < pop.getIndividuals().size(); i++) {
|
||||||
|
Individual indiv1 = tournamentSelection(pop);
|
||||||
|
Individual indiv2 = tournamentSelection(pop);
|
||||||
|
Individual newIndiv = crossover(indiv1, indiv2);
|
||||||
|
newPopulation.getIndividuals().add(i, newIndiv);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = elitismOffset; i < newPopulation.getIndividuals().size(); i++) {
|
||||||
|
mutate(newPopulation.getIndividual(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
return newPopulation;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Individual crossover(Individual indiv1, Individual indiv2) {
|
||||||
|
Individual newSol = new Individual();
|
||||||
|
for (int i = 0; i < newSol.getDefaultGeneLength(); i++) {
|
||||||
|
if (Math.random() <= uniformRate) {
|
||||||
|
newSol.setSingleGene(i, indiv1.getSingleGene(i));
|
||||||
|
} else {
|
||||||
|
newSol.setSingleGene(i, indiv2.getSingleGene(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newSol;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mutate(Individual indiv) {
|
||||||
|
for (int i = 0; i < indiv.getDefaultGeneLength(); i++) {
|
||||||
|
if (Math.random() <= mutationRate) {
|
||||||
|
byte gene = (byte) Math.round(Math.random());
|
||||||
|
indiv.setSingleGene(i, gene);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Individual tournamentSelection(Population pop) {
|
||||||
|
Population tournament = new Population(tournamentSize, false);
|
||||||
|
for (int i = 0; i < tournamentSize; i++) {
|
||||||
|
int randomId = (int) (Math.random() * pop.getIndividuals().size());
|
||||||
|
tournament.getIndividuals().add(i, pop.getIndividual(randomId));
|
||||||
|
}
|
||||||
|
Individual fittest = tournament.getFittest();
|
||||||
|
return fittest;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static int getFitness(Individual individual) {
|
||||||
|
int fitness = 0;
|
||||||
|
for (int i = 0; i < individual.getDefaultGeneLength() && i < solution.length; i++) {
|
||||||
|
if (individual.getSingleGene(i) == solution[i]) {
|
||||||
|
fitness++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fitness;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getMaxFitness() {
|
||||||
|
int maxFitness = solution.length;
|
||||||
|
return maxFitness;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setSolution(String newSolution) {
|
||||||
|
solution = new byte[newSolution.length()];
|
||||||
|
for (int i = 0; i < newSolution.length(); i++) {
|
||||||
|
String character = newSolution.substring(i, i + 1);
|
||||||
|
if (character.contains("0") || character.contains("1")) {
|
||||||
|
solution[i] = Byte.parseByte(character);
|
||||||
|
} else {
|
||||||
|
solution[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package com.baeldung.algorithms.ga.jenetics;
|
||||||
|
|
||||||
|
import static org.jenetics.engine.EvolutionResult.toBestPhenotype;
|
||||||
|
import static org.jenetics.engine.limit.bySteadyFitness;
|
||||||
|
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.jenetics.BitChromosome;
|
||||||
|
import org.jenetics.BitGene;
|
||||||
|
import org.jenetics.Mutator;
|
||||||
|
import org.jenetics.Phenotype;
|
||||||
|
import org.jenetics.RouletteWheelSelector;
|
||||||
|
import org.jenetics.SinglePointCrossover;
|
||||||
|
import org.jenetics.TournamentSelector;
|
||||||
|
import org.jenetics.engine.Engine;
|
||||||
|
import org.jenetics.engine.EvolutionStatistics;
|
||||||
|
|
||||||
|
//The main class.
|
||||||
|
public class Knapsack {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
int nItems = 15;
|
||||||
|
double ksSize = nItems * 100.0 / 3.0;
|
||||||
|
|
||||||
|
KnapsackFF ff = new KnapsackFF(Stream.generate(KnapsackItem::random)
|
||||||
|
.limit(nItems)
|
||||||
|
.toArray(KnapsackItem[]::new), ksSize);
|
||||||
|
|
||||||
|
Engine<BitGene, Double> engine = Engine.builder(ff, BitChromosome.of(nItems, 0.5))
|
||||||
|
.populationSize(500)
|
||||||
|
.survivorsSelector(new TournamentSelector<>(5))
|
||||||
|
.offspringSelector(new RouletteWheelSelector<>())
|
||||||
|
.alterers(new Mutator<>(0.115), new SinglePointCrossover<>(0.16))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
EvolutionStatistics<Double, ?> statistics = EvolutionStatistics.ofNumber();
|
||||||
|
|
||||||
|
Phenotype<BitGene, Double> best = engine.stream()
|
||||||
|
.limit(bySteadyFitness(7))
|
||||||
|
.limit(100)
|
||||||
|
.peek(statistics)
|
||||||
|
.collect(toBestPhenotype());
|
||||||
|
|
||||||
|
System.out.println(statistics);
|
||||||
|
System.out.println(best);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package com.baeldung.algorithms.ga.jenetics;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import org.jenetics.BitChromosome;
|
||||||
|
import org.jenetics.BitGene;
|
||||||
|
import org.jenetics.Genotype;
|
||||||
|
|
||||||
|
public class KnapsackFF implements Function<Genotype<BitGene>, Double> {
|
||||||
|
private KnapsackItem[] items;
|
||||||
|
private double size;
|
||||||
|
|
||||||
|
public KnapsackFF(KnapsackItem[] items, double size) {
|
||||||
|
this.items = items;
|
||||||
|
this.size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Double apply(Genotype<BitGene> gt) {
|
||||||
|
KnapsackItem sum = ((BitChromosome) gt.getChromosome()).ones()
|
||||||
|
.mapToObj(i -> items[i])
|
||||||
|
.collect(KnapsackItem.toSum());
|
||||||
|
return sum.size <= this.size ? sum.value : 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package com.baeldung.algorithms.ga.jenetics;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.stream.Collector;
|
||||||
|
|
||||||
|
import org.jenetics.util.RandomRegistry;
|
||||||
|
|
||||||
|
public class KnapsackItem {
|
||||||
|
|
||||||
|
public double size;
|
||||||
|
public double value;
|
||||||
|
|
||||||
|
public KnapsackItem(double size, double value) {
|
||||||
|
this.size = size;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static KnapsackItem random() {
|
||||||
|
Random r = RandomRegistry.getRandom();
|
||||||
|
return new KnapsackItem(r.nextDouble() * 100, r.nextDouble() * 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static Collector<KnapsackItem, ?, KnapsackItem> toSum() {
|
||||||
|
return Collector.of(() -> new double[2], (a, b) -> {
|
||||||
|
a[0] += b.size;
|
||||||
|
a[1] += b.value;
|
||||||
|
} , (a, b) -> {
|
||||||
|
a[0] += b[0];
|
||||||
|
a[1] += b[1];
|
||||||
|
return a;
|
||||||
|
} , r -> new KnapsackItem(r[0], r[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.baeldung.algorithms.ga.jenetics;
|
||||||
|
|
||||||
|
import org.jenetics.BitChromosome;
|
||||||
|
import org.jenetics.BitGene;
|
||||||
|
import org.jenetics.Genotype;
|
||||||
|
import org.jenetics.engine.Engine;
|
||||||
|
import org.jenetics.engine.EvolutionResult;
|
||||||
|
import org.jenetics.util.Factory;
|
||||||
|
|
||||||
|
public class SimpleGeneticAlgorithm {
|
||||||
|
|
||||||
|
private static Integer eval(Genotype<BitGene> gt) {
|
||||||
|
return gt.getChromosome()
|
||||||
|
.as(BitChromosome.class)
|
||||||
|
.bitCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Factory<Genotype<BitGene>> gtf = Genotype.of(BitChromosome.of(10, 0.5));
|
||||||
|
System.out.println("Before the evolution:\n" + gtf);
|
||||||
|
|
||||||
|
Engine<BitGene, Integer> engine = Engine.builder(SimpleGeneticAlgorithm::eval, gtf)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Genotype<BitGene> result = engine.stream()
|
||||||
|
.limit(500)
|
||||||
|
.collect(EvolutionResult.toBestGenotype());
|
||||||
|
|
||||||
|
System.out.println("After the evolution:\n" + result);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
package com.baeldung.algorithms.ga.jenetics;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.jenetics.BitGene;
|
||||||
|
import org.jenetics.engine.Codec;
|
||||||
|
import org.jenetics.engine.Engine;
|
||||||
|
import org.jenetics.engine.EvolutionResult;
|
||||||
|
import org.jenetics.engine.Problem;
|
||||||
|
import org.jenetics.engine.codecs;
|
||||||
|
import org.jenetics.util.ISeq;
|
||||||
|
|
||||||
|
public class SpringsteenProblem implements Problem<ISeq<SpringsteenRecord>, BitGene, Double> {
|
||||||
|
|
||||||
|
private ISeq<SpringsteenRecord> records;
|
||||||
|
private double maxPricePerUniqueSong;
|
||||||
|
|
||||||
|
public SpringsteenProblem(ISeq<SpringsteenRecord> records, double maxPricePerUniqueSong) {
|
||||||
|
this.records = requireNonNull(records);
|
||||||
|
this.maxPricePerUniqueSong = maxPricePerUniqueSong;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Function<ISeq<SpringsteenRecord>, Double> fitness() {
|
||||||
|
return SpringsteenRecords -> {
|
||||||
|
double cost = SpringsteenRecords.stream()
|
||||||
|
.mapToDouble(r -> r.price)
|
||||||
|
.sum();
|
||||||
|
|
||||||
|
int uniqueSongCount = SpringsteenRecords.stream()
|
||||||
|
.flatMap(r -> r.songs.stream())
|
||||||
|
.collect(Collectors.toSet())
|
||||||
|
.size();
|
||||||
|
|
||||||
|
double pricePerUniqueSong = cost / uniqueSongCount;
|
||||||
|
|
||||||
|
return pricePerUniqueSong <= maxPricePerUniqueSong ? uniqueSongCount : 0.0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Codec<ISeq<SpringsteenRecord>, BitGene> codec() {
|
||||||
|
return codecs.ofSubSet(records);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
double maxPricePerUniqueSong = 2.5;
|
||||||
|
|
||||||
|
SpringsteenProblem springsteen = new SpringsteenProblem(
|
||||||
|
ISeq.of(new SpringsteenRecord("SpringsteenRecord1", 25, ISeq.of("Song1", "Song2", "Song3", "Song4", "Song5", "Song6")), new SpringsteenRecord("SpringsteenRecord2", 15, ISeq.of("Song2", "Song3", "Song4", "Song5", "Song6", "Song7")),
|
||||||
|
new SpringsteenRecord("SpringsteenRecord3", 35, ISeq.of("Song5", "Song6", "Song7", "Song8", "Song9", "Song10")), new SpringsteenRecord("SpringsteenRecord4", 17, ISeq.of("Song9", "Song10", "Song12", "Song4", "Song13", "Song14")),
|
||||||
|
new SpringsteenRecord("SpringsteenRecord5", 29, ISeq.of("Song1", "Song2", "Song13", "Song14", "Song15", "Song16")), new SpringsteenRecord("SpringsteenRecord6", 5, ISeq.of("Song18", "Song20", "Song30", "Song40"))),
|
||||||
|
maxPricePerUniqueSong);
|
||||||
|
|
||||||
|
Engine<BitGene, Double> engine = Engine.builder(springsteen)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
ISeq<SpringsteenRecord> result = springsteen.codec()
|
||||||
|
.decoder()
|
||||||
|
.apply(engine.stream()
|
||||||
|
.limit(10)
|
||||||
|
.collect(EvolutionResult.toBestGenotype()));
|
||||||
|
|
||||||
|
double cost = result.stream()
|
||||||
|
.mapToDouble(r -> r.price)
|
||||||
|
.sum();
|
||||||
|
|
||||||
|
int uniqueSongCount = result.stream()
|
||||||
|
.flatMap(r -> r.songs.stream())
|
||||||
|
.collect(Collectors.toSet())
|
||||||
|
.size();
|
||||||
|
|
||||||
|
double pricePerUniqueSong = cost / uniqueSongCount;
|
||||||
|
|
||||||
|
System.out.println("Overall cost: " + cost);
|
||||||
|
System.out.println("Unique songs: " + uniqueSongCount);
|
||||||
|
System.out.println("Cost per song: " + pricePerUniqueSong);
|
||||||
|
System.out.println("Records: " + result.map(r -> r.name)
|
||||||
|
.toString(", "));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.baeldung.algorithms.ga.jenetics;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
|
import org.jenetics.util.ISeq;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class SpringsteenRecord {
|
||||||
|
|
||||||
|
String name;
|
||||||
|
double price;
|
||||||
|
ISeq<String> songs;
|
||||||
|
|
||||||
|
public SpringsteenRecord(String name, double price, ISeq<String> songs) {
|
||||||
|
this.name = requireNonNull(name);
|
||||||
|
this.price = price;
|
||||||
|
this.songs = requireNonNull(songs);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
package com.baeldung.algorithms.ga.jenetics;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import org.jenetics.EnumGene;
|
||||||
|
import org.jenetics.Mutator;
|
||||||
|
import org.jenetics.PartiallyMatchedCrossover;
|
||||||
|
import org.jenetics.Phenotype;
|
||||||
|
import org.jenetics.engine.Codec;
|
||||||
|
import org.jenetics.engine.Engine;
|
||||||
|
import org.jenetics.engine.EvolutionResult;
|
||||||
|
import org.jenetics.engine.Problem;
|
||||||
|
import org.jenetics.engine.codecs;
|
||||||
|
import org.jenetics.engine.limit;
|
||||||
|
import org.jenetics.util.ISeq;
|
||||||
|
import org.jenetics.util.LCG64ShiftRandom;
|
||||||
|
|
||||||
|
public class SubsetSum implements Problem<ISeq<Integer>, EnumGene<Integer>, Integer> {
|
||||||
|
|
||||||
|
private ISeq<Integer> basicSet;
|
||||||
|
private int size;
|
||||||
|
|
||||||
|
public SubsetSum(ISeq<Integer> basicSet, int size) {
|
||||||
|
this.basicSet = requireNonNull(basicSet);
|
||||||
|
this.size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Function<ISeq<Integer>, Integer> fitness() {
|
||||||
|
return subset -> Math.abs(subset.stream()
|
||||||
|
.mapToInt(Integer::intValue)
|
||||||
|
.sum());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Codec<ISeq<Integer>, EnumGene<Integer>> codec() {
|
||||||
|
return codecs.ofSubSet(basicSet, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SubsetSum of(int n, int k, Random random) {
|
||||||
|
return new SubsetSum(random.doubles()
|
||||||
|
.limit(n)
|
||||||
|
.mapToObj(d -> (int) ((d - 0.5) * n))
|
||||||
|
.collect(ISeq.toISeq()), k);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SubsetSum problem = of(500, 15, new LCG64ShiftRandom(101010));
|
||||||
|
|
||||||
|
Engine<EnumGene<Integer>, Integer> engine = Engine.builder(problem)
|
||||||
|
.minimizing()
|
||||||
|
.maximalPhenotypeAge(5)
|
||||||
|
.alterers(new PartiallyMatchedCrossover<>(0.4), new Mutator<>(0.3))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Phenotype<EnumGene<Integer>, Integer> result = engine.stream()
|
||||||
|
.limit(limit.bySteadyFitness(55))
|
||||||
|
.collect(EvolutionResult.toBestPhenotype());
|
||||||
|
|
||||||
|
System.out.print(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
package com.baeldung.algorithms.ga.jenetics;
|
||||||
|
|
||||||
|
import static java.lang.Math.PI;
|
||||||
|
import static java.lang.Math.abs;
|
||||||
|
import static java.lang.Math.sin;
|
||||||
|
import static org.jenetics.engine.EvolutionResult.toBestPhenotype;
|
||||||
|
import static org.jenetics.engine.limit.bySteadyFitness;
|
||||||
|
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
|
import org.jenetics.EnumGene;
|
||||||
|
import org.jenetics.Optimize;
|
||||||
|
import org.jenetics.PartiallyMatchedCrossover;
|
||||||
|
import org.jenetics.Phenotype;
|
||||||
|
import org.jenetics.SwapMutator;
|
||||||
|
import org.jenetics.engine.Engine;
|
||||||
|
import org.jenetics.engine.EvolutionStatistics;
|
||||||
|
import org.jenetics.engine.codecs;
|
||||||
|
|
||||||
|
public class TravelingSalesman {
|
||||||
|
|
||||||
|
private static final int STOPS = 50;
|
||||||
|
private static final double[][] ADJACENCE = matrix(STOPS);
|
||||||
|
|
||||||
|
private static double[][] matrix(int stops) {
|
||||||
|
final double radius = 100.0;
|
||||||
|
double[][] matrix = new double[stops][stops];
|
||||||
|
|
||||||
|
for (int i = 0; i < stops; ++i) {
|
||||||
|
for (int j = 0; j < stops; ++j) {
|
||||||
|
matrix[i][j] = chord(stops, abs(i - j), radius);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double chord(int stops, int i, double r) {
|
||||||
|
return 2.0 * r * abs(sin(PI * i / stops));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double dist(final int[] path) {
|
||||||
|
return IntStream.range(0, STOPS)
|
||||||
|
.mapToDouble(i -> ADJACENCE[path[i]][path[(i + 1) % STOPS]])
|
||||||
|
.sum();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
final Engine<EnumGene<Integer>, Double> engine = Engine.builder(TravelingSalesman::dist, codecs.ofPermutation(STOPS))
|
||||||
|
.optimize(Optimize.MINIMUM)
|
||||||
|
.maximalPhenotypeAge(11)
|
||||||
|
.populationSize(500)
|
||||||
|
.alterers(new SwapMutator<>(0.2), new PartiallyMatchedCrossover<>(0.35))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final EvolutionStatistics<Double, ?> statistics = EvolutionStatistics.ofNumber();
|
||||||
|
|
||||||
|
final Phenotype<EnumGene<Integer>, Double> best = engine.stream()
|
||||||
|
.limit(bySteadyFitness(15))
|
||||||
|
.limit(250)
|
||||||
|
.peek(statistics)
|
||||||
|
.collect(toBestPhenotype());
|
||||||
|
|
||||||
|
System.out.println(statistics);
|
||||||
|
System.out.println(best);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<configuration>
|
||||||
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<encoder>
|
||||||
|
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
|
||||||
|
</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<root level="INFO">
|
||||||
|
<appender-ref ref="STDOUT" />
|
||||||
|
</root>
|
||||||
|
</configuration>
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.baeldung.algorithms;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import com.baeldung.algorithms.ga.ant_colony.AntColonyOptimization;
|
||||||
|
|
||||||
|
public class AntColonyOptimizationLongRunningUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGenerateRandomMatrix() {
|
||||||
|
AntColonyOptimization antTSP = new AntColonyOptimization(5);
|
||||||
|
Assert.assertNotNull(antTSP.generateRandomMatrix(5));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStartAntOptimization() {
|
||||||
|
AntColonyOptimization antTSP = new AntColonyOptimization(5);
|
||||||
|
Assert.assertNotNull(antTSP.solve());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.baeldung.algorithms;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import com.baeldung.algorithms.ga.binary.SimpleGeneticAlgorithm;
|
||||||
|
|
||||||
|
public class BinaryGeneticAlgorithmLongRunningUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGA() {
|
||||||
|
SimpleGeneticAlgorithm ga = new SimpleGeneticAlgorithm();
|
||||||
|
Assert.assertTrue(ga.runAlgorithm(50, "1011000100000100010000100000100111001000000100000100000000001111"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.baeldung.algorithms;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import com.baeldung.algorithms.ga.annealing.SimulatedAnnealing;
|
||||||
|
|
||||||
|
public class SimulatedAnnealingLongRunningUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSimulateAnnealing() {
|
||||||
|
Assert.assertTrue(SimulatedAnnealing.simulateAnnealing(10, 1000, 0.9) > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
## Algorithms - Miscellaneous
|
||||||
|
|
||||||
|
This module contains articles about algorithms. Some classes of algorithms, e.g., [sorting](/../algorithms-sorting) and
|
||||||
|
[genetic algorithms](/../algorithms-genetic), have their own dedicated modules.
|
||||||
|
|
||||||
|
### Relevant articles:
|
||||||
|
|
||||||
|
- [Validating Input With Finite Automata in Java](https://www.baeldung.com/java-finite-automata)
|
||||||
|
- [Example of Hill Climbing Algorithm](https://www.baeldung.com/java-hill-climbing-algorithm)
|
||||||
|
- [Monte Carlo Tree Search for Tic-Tac-Toe Game](https://www.baeldung.com/java-monte-carlo-tree-search)
|
||||||
|
- [Binary Search Algorithm in Java](https://www.baeldung.com/java-binary-search)
|
||||||
|
- [Introduction to Minimax Algorithm](https://www.baeldung.com/java-minimax-algorithm)
|
||||||
|
- [How to Calculate Levenshtein Distance in Java?](https://www.baeldung.com/java-levenshtein-distance)
|
||||||
|
- [How to Find the Kth Largest Element in Java](https://www.baeldung.com/java-kth-largest-element)
|
||||||
|
- More articles: [[next -->]](/../algorithms-miscellaneous-2)
|
|
@ -0,0 +1,89 @@
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>algorithms-miscellaneous-1</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<name>algorithms-miscellaneous-1</name>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<artifactId>parent-modules</artifactId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-math3</artifactId>
|
||||||
|
<version>${commons-math3.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.guava</groupId>
|
||||||
|
<artifactId>guava</artifactId>
|
||||||
|
<version>${guava.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-codec</groupId>
|
||||||
|
<artifactId>commons-codec</artifactId>
|
||||||
|
<version>${commons-codec.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>${lombok.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.assertj</groupId>
|
||||||
|
<artifactId>assertj-core</artifactId>
|
||||||
|
<version>${org.assertj.core.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.dpaukov</groupId>
|
||||||
|
<artifactId>combinatoricslib3</artifactId>
|
||||||
|
<version>${combinatoricslib3.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<pluginManagement>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
|
<artifactId>exec-maven-plugin</artifactId>
|
||||||
|
<version>${exec-maven-plugin.version}</version>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</pluginManagement>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<reporting>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
|
<artifactId>cobertura-maven-plugin</artifactId>
|
||||||
|
<version>2.7</version>
|
||||||
|
<configuration>
|
||||||
|
<instrumentation>
|
||||||
|
<ignores>
|
||||||
|
<ignore>com/baeldung/algorithms/dijkstra/*</ignore>
|
||||||
|
</ignores>
|
||||||
|
<excludes>
|
||||||
|
<exclude>com/baeldung/algorithms/dijkstra/*</exclude>
|
||||||
|
</excludes>
|
||||||
|
</instrumentation>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</reporting>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<commons-math3.version>3.6.1</commons-math3.version>
|
||||||
|
<org.assertj.core.version>3.9.0</org.assertj.core.version>
|
||||||
|
<commons-codec.version>1.11</commons-codec.version>
|
||||||
|
<guava.version>27.0.1-jre</guava.version>
|
||||||
|
<combinatoricslib3.version>3.3.0</combinatoricslib3.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.baeldung.algorithms.automata;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finite state machine.
|
||||||
|
*/
|
||||||
|
public interface FiniteStateMachine {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Follow a transition, switch the state of the machine.
|
||||||
|
* @param c Char.
|
||||||
|
* @return A new finite state machine with the new state.
|
||||||
|
*/
|
||||||
|
FiniteStateMachine switchState(final CharSequence c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the current state a final one?
|
||||||
|
* @return true or false.
|
||||||
|
*/
|
||||||
|
boolean canStop();
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.baeldung.algorithms.automata;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default implementation of a finite state machine.
|
||||||
|
* This class is immutable and thread-safe.
|
||||||
|
*/
|
||||||
|
public final class RtFiniteStateMachine implements FiniteStateMachine {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current state.
|
||||||
|
*/
|
||||||
|
private State current;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ctor.
|
||||||
|
* @param initial Initial state of this machine.
|
||||||
|
*/
|
||||||
|
public RtFiniteStateMachine(final State initial) {
|
||||||
|
this.current = initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FiniteStateMachine switchState(final CharSequence c) {
|
||||||
|
return new RtFiniteStateMachine(this.current.transit(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canStop() {
|
||||||
|
return this.current.isFinal();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package com.baeldung.algorithms.automata;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State in a finite state machine.
|
||||||
|
*/
|
||||||
|
public final class RtState implements State {
|
||||||
|
|
||||||
|
private List<Transition> transitions;
|
||||||
|
private boolean isFinal;
|
||||||
|
|
||||||
|
public RtState() {
|
||||||
|
this(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RtState(final boolean isFinal) {
|
||||||
|
this.transitions = new ArrayList<>();
|
||||||
|
this.isFinal = isFinal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public State transit(final CharSequence c) {
|
||||||
|
return transitions
|
||||||
|
.stream()
|
||||||
|
.filter(t -> t.isPossible(c))
|
||||||
|
.map(Transition::state)
|
||||||
|
.findAny()
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("Input not accepted: " + c));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFinal() {
|
||||||
|
return this.isFinal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public State with(Transition tr) {
|
||||||
|
this.transitions.add(tr);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package com.baeldung.algorithms.automata;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transition in finite state machine.
|
||||||
|
*/
|
||||||
|
public final class RtTransition implements Transition {
|
||||||
|
|
||||||
|
private String rule;
|
||||||
|
private State next;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ctor.
|
||||||
|
* @param rule Rule that a character has to meet
|
||||||
|
* in order to get to the next state.
|
||||||
|
* @param next Next state.
|
||||||
|
*/
|
||||||
|
public RtTransition (String rule, State next) {
|
||||||
|
this.rule = rule;
|
||||||
|
this.next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
public State state() {
|
||||||
|
return this.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPossible(CharSequence c) {
|
||||||
|
return this.rule.equalsIgnoreCase(String.valueOf(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package com.baeldung.algorithms.automata;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State. Part of a finite state machine.
|
||||||
|
*/
|
||||||
|
public interface State {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a Transition to this state.
|
||||||
|
* @param tr Given transition.
|
||||||
|
* @return Modified State.
|
||||||
|
*/
|
||||||
|
State with(final Transition tr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Follow one of the transitions, to get
|
||||||
|
* to the next state.
|
||||||
|
* @param c Character.
|
||||||
|
* @return State.
|
||||||
|
* @throws IllegalStateException if the char is not accepted.
|
||||||
|
*/
|
||||||
|
State transit(final CharSequence c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can the automaton stop on this state?
|
||||||
|
* @return true or false
|
||||||
|
*/
|
||||||
|
boolean isFinal();
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.baeldung.algorithms.automata;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transition in a finite State machine.
|
||||||
|
*/
|
||||||
|
public interface Transition {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the transition possible with the given character?
|
||||||
|
* @param c char.
|
||||||
|
* @return true or false.
|
||||||
|
*/
|
||||||
|
boolean isPossible(final CharSequence c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The state to which this transition leads.
|
||||||
|
* @return State.
|
||||||
|
*/
|
||||||
|
State state();
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
package com.baeldung.algorithms.binarysearch;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class BinarySearch {
|
||||||
|
|
||||||
|
public int runBinarySearchIteratively(int[] sortedArray, int key, int low, int high) {
|
||||||
|
|
||||||
|
int index = Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
while (low <= high) {
|
||||||
|
|
||||||
|
int mid = (low + high) / 2;
|
||||||
|
|
||||||
|
if (sortedArray[mid] < key) {
|
||||||
|
low = mid + 1;
|
||||||
|
} else if (sortedArray[mid] > key) {
|
||||||
|
high = mid - 1;
|
||||||
|
} else if (sortedArray[mid] == key) {
|
||||||
|
index = mid;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int runBinarySearchRecursively(int[] sortedArray, int key, int low, int high) {
|
||||||
|
|
||||||
|
int middle = (low + high) / 2;
|
||||||
|
if (high < low) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key == sortedArray[middle]) {
|
||||||
|
return middle;
|
||||||
|
} else if (key < sortedArray[middle]) {
|
||||||
|
return runBinarySearchRecursively(sortedArray, key, low, middle - 1);
|
||||||
|
} else {
|
||||||
|
return runBinarySearchRecursively(sortedArray, key, middle + 1, high);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int runBinarySearchUsingJavaArrays(int[] sortedArray, Integer key) {
|
||||||
|
int index = Arrays.binarySearch(sortedArray, key);
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int runBinarySearchUsingJavaCollections(List<Integer> sortedList, Integer key) {
|
||||||
|
int index = Collections.binarySearch(sortedList, key);
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,189 @@
|
||||||
|
package com.baeldung.algorithms.hillclimbing;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
public class HillClimbing {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
HillClimbing hillClimbing = new HillClimbing();
|
||||||
|
String blockArr[] = { "B", "C", "D", "A" };
|
||||||
|
Stack<String> startState = hillClimbing.getStackWithValues(blockArr);
|
||||||
|
String goalBlockArr[] = { "A", "B", "C", "D" };
|
||||||
|
Stack<String> goalState = hillClimbing.getStackWithValues(goalBlockArr);
|
||||||
|
try {
|
||||||
|
List<State> solutionSequence = hillClimbing.getRouteWithHillClimbing(startState, goalState);
|
||||||
|
solutionSequence.forEach(HillClimbing::printEachStep);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void printEachStep(State state) {
|
||||||
|
List<Stack<String>> stackList = state.getState();
|
||||||
|
System.out.println("----------------");
|
||||||
|
stackList.forEach(stack -> {
|
||||||
|
while (!stack.isEmpty()) {
|
||||||
|
System.out.println(stack.pop());
|
||||||
|
}
|
||||||
|
System.out.println(" ");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stack<String> getStackWithValues(String[] blocks) {
|
||||||
|
Stack<String> stack = new Stack<>();
|
||||||
|
for (String block : blocks)
|
||||||
|
stack.push(block);
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method prepares path from init state to goal state
|
||||||
|
*/
|
||||||
|
public List<State> getRouteWithHillClimbing(Stack<String> initStateStack, Stack<String> goalStateStack) throws Exception {
|
||||||
|
List<Stack<String>> initStateStackList = new ArrayList<>();
|
||||||
|
initStateStackList.add(initStateStack);
|
||||||
|
int initStateHeuristics = getHeuristicsValue(initStateStackList, goalStateStack);
|
||||||
|
State initState = new State(initStateStackList, initStateHeuristics);
|
||||||
|
|
||||||
|
List<State> resultPath = new ArrayList<>();
|
||||||
|
resultPath.add(new State(initState));
|
||||||
|
|
||||||
|
State currentState = initState;
|
||||||
|
boolean noStateFound = false;
|
||||||
|
while (!currentState.getState()
|
||||||
|
.get(0)
|
||||||
|
.equals(goalStateStack) || noStateFound) {
|
||||||
|
noStateFound = true;
|
||||||
|
State nextState = findNextState(currentState, goalStateStack);
|
||||||
|
if (nextState != null) {
|
||||||
|
noStateFound = false;
|
||||||
|
currentState = nextState;
|
||||||
|
resultPath.add(new State(nextState));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method finds new state from current state based on goal and
|
||||||
|
* heuristics
|
||||||
|
*/
|
||||||
|
public State findNextState(State currentState, Stack<String> goalStateStack) {
|
||||||
|
List<Stack<String>> listOfStacks = currentState.getState();
|
||||||
|
int currentStateHeuristics = currentState.getHeuristics();
|
||||||
|
|
||||||
|
return listOfStacks.stream()
|
||||||
|
.map(stack -> {
|
||||||
|
return applyOperationsOnState(listOfStacks, stack, currentStateHeuristics, goalStateStack);
|
||||||
|
})
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method applies operations on the current state to get a new state
|
||||||
|
*/
|
||||||
|
public State applyOperationsOnState(List<Stack<String>> listOfStacks, Stack<String> stack, int currentStateHeuristics, Stack<String> goalStateStack) {
|
||||||
|
State tempState;
|
||||||
|
List<Stack<String>> tempStackList = new ArrayList<>(listOfStacks);
|
||||||
|
String block = stack.pop();
|
||||||
|
if (stack.size() == 0)
|
||||||
|
tempStackList.remove(stack);
|
||||||
|
tempState = pushElementToNewStack(tempStackList, block, currentStateHeuristics, goalStateStack);
|
||||||
|
if (tempState == null) {
|
||||||
|
tempState = pushElementToExistingStacks(stack, tempStackList, block, currentStateHeuristics, goalStateStack);
|
||||||
|
}
|
||||||
|
if (tempState == null)
|
||||||
|
stack.push(block);
|
||||||
|
return tempState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Operation to be applied on a state in order to find new states. This
|
||||||
|
* operation pushes an element into a new stack
|
||||||
|
*/
|
||||||
|
private State pushElementToNewStack(List<Stack<String>> currentStackList, String block, int currentStateHeuristics, Stack<String> goalStateStack) {
|
||||||
|
State newState = null;
|
||||||
|
Stack<String> newStack = new Stack<>();
|
||||||
|
newStack.push(block);
|
||||||
|
|
||||||
|
currentStackList.add(newStack);
|
||||||
|
int newStateHeuristics = getHeuristicsValue(currentStackList, goalStateStack);
|
||||||
|
if (newStateHeuristics > currentStateHeuristics) {
|
||||||
|
newState = new State(currentStackList, newStateHeuristics);
|
||||||
|
} else {
|
||||||
|
currentStackList.remove(newStack);
|
||||||
|
}
|
||||||
|
return newState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Operation to be applied on a state in order to find new states. This
|
||||||
|
* operation pushes an element into one of the other stacks to explore new
|
||||||
|
* states
|
||||||
|
*/
|
||||||
|
private State pushElementToExistingStacks(Stack currentStack, List<Stack<String>> currentStackList, String block, int currentStateHeuristics, Stack<String> goalStateStack) {
|
||||||
|
|
||||||
|
Optional<State> newState = currentStackList.stream()
|
||||||
|
.filter(stack -> stack != currentStack)
|
||||||
|
.map(stack -> {
|
||||||
|
return pushElementToStack(stack, block, currentStackList, currentStateHeuristics, goalStateStack);
|
||||||
|
})
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.findFirst();
|
||||||
|
|
||||||
|
return newState.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method pushes a block to the stack and returns new state if its closer to goal
|
||||||
|
*/
|
||||||
|
private State pushElementToStack(Stack stack, String block, List<Stack<String>> currentStackList, int currentStateHeuristics, Stack<String> goalStateStack) {
|
||||||
|
stack.push(block);
|
||||||
|
int newStateHeuristics = getHeuristicsValue(currentStackList, goalStateStack);
|
||||||
|
if (newStateHeuristics > currentStateHeuristics) {
|
||||||
|
return new State(currentStackList, newStateHeuristics);
|
||||||
|
}
|
||||||
|
stack.pop();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns heuristics value for given state with respect to goal
|
||||||
|
* state
|
||||||
|
*/
|
||||||
|
public int getHeuristicsValue(List<Stack<String>> currentState, Stack<String> goalStateStack) {
|
||||||
|
Integer heuristicValue;
|
||||||
|
heuristicValue = currentState.stream()
|
||||||
|
.mapToInt(stack -> {
|
||||||
|
return getHeuristicsValueForStack(stack, currentState, goalStateStack);
|
||||||
|
})
|
||||||
|
.sum();
|
||||||
|
return heuristicValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns heuristics value for a particular stack
|
||||||
|
*/
|
||||||
|
public int getHeuristicsValueForStack(Stack<String> stack, List<Stack<String>> currentState, Stack<String> goalStateStack) {
|
||||||
|
int stackHeuristics = 0;
|
||||||
|
boolean isPositioneCorrect = true;
|
||||||
|
int goalStartIndex = 0;
|
||||||
|
for (String currentBlock : stack) {
|
||||||
|
if (isPositioneCorrect && currentBlock.equals(goalStateStack.get(goalStartIndex))) {
|
||||||
|
stackHeuristics += goalStartIndex;
|
||||||
|
} else {
|
||||||
|
stackHeuristics -= goalStartIndex;
|
||||||
|
isPositioneCorrect = false;
|
||||||
|
}
|
||||||
|
goalStartIndex++;
|
||||||
|
}
|
||||||
|
return stackHeuristics;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
package com.baeldung.algorithms.hillclimbing;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
public class State {
|
||||||
|
private List<Stack<String>> state;
|
||||||
|
private int heuristics;
|
||||||
|
|
||||||
|
public State(List<Stack<String>> state) {
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
State(List<Stack<String>> state, int heuristics) {
|
||||||
|
this.state = state;
|
||||||
|
this.heuristics = heuristics;
|
||||||
|
}
|
||||||
|
|
||||||
|
State(State state) {
|
||||||
|
if (state != null) {
|
||||||
|
this.state = new ArrayList<>();
|
||||||
|
for (Stack s : state.getState()) {
|
||||||
|
Stack s1;
|
||||||
|
s1 = (Stack) s.clone();
|
||||||
|
this.state.add(s1);
|
||||||
|
}
|
||||||
|
this.heuristics = state.getHeuristics();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Stack<String>> getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHeuristics() {
|
||||||
|
return heuristics;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHeuristics(int heuristics) {
|
||||||
|
this.heuristics = heuristics;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,111 @@
|
||||||
|
package com.baeldung.algorithms.kthlargest;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
|
public class FindKthLargest {
|
||||||
|
|
||||||
|
public int findKthLargestBySorting(Integer[] arr, int k) {
|
||||||
|
Arrays.sort(arr);
|
||||||
|
int targetIndex = arr.length - k;
|
||||||
|
return arr[targetIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int findKthLargestBySortingDesc(Integer[] arr, int k) {
|
||||||
|
Arrays.sort(arr, Collections.reverseOrder());
|
||||||
|
return arr[k - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int findKthElementByQuickSelect(Integer[] arr, int left, int right, int k) {
|
||||||
|
if (k >= 0 && k <= right - left + 1) {
|
||||||
|
int pos = partition(arr, left, right);
|
||||||
|
if (pos - left == k) {
|
||||||
|
return arr[pos];
|
||||||
|
}
|
||||||
|
if (pos - left > k) {
|
||||||
|
return findKthElementByQuickSelect(arr, left, pos - 1, k);
|
||||||
|
}
|
||||||
|
return findKthElementByQuickSelect(arr, pos + 1, right, k - pos + left - 1);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int findKthElementByQuickSelectWithIterativePartition(Integer[] arr, int left, int right, int k) {
|
||||||
|
if (k >= 0 && k <= right - left + 1) {
|
||||||
|
int pos = partitionIterative(arr, left, right);
|
||||||
|
if (pos - left == k) {
|
||||||
|
return arr[pos];
|
||||||
|
}
|
||||||
|
if (pos - left > k) {
|
||||||
|
return findKthElementByQuickSelectWithIterativePartition(arr, left, pos - 1, k);
|
||||||
|
}
|
||||||
|
return findKthElementByQuickSelectWithIterativePartition(arr, pos + 1, right, k - pos + left - 1);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int partition(Integer[] arr, int left, int right) {
|
||||||
|
int pivot = arr[right];
|
||||||
|
Integer[] leftArr;
|
||||||
|
Integer[] rightArr;
|
||||||
|
|
||||||
|
leftArr = IntStream.range(left, right)
|
||||||
|
.filter(i -> arr[i] < pivot)
|
||||||
|
.map(i -> arr[i])
|
||||||
|
.boxed()
|
||||||
|
.toArray(Integer[]::new);
|
||||||
|
|
||||||
|
rightArr = IntStream.range(left, right)
|
||||||
|
.filter(i -> arr[i] > pivot)
|
||||||
|
.map(i -> arr[i])
|
||||||
|
.boxed()
|
||||||
|
.toArray(Integer[]::new);
|
||||||
|
|
||||||
|
int leftArraySize = leftArr.length;
|
||||||
|
System.arraycopy(leftArr, 0, arr, left, leftArraySize);
|
||||||
|
arr[leftArraySize + left] = pivot;
|
||||||
|
System.arraycopy(rightArr, 0, arr, left + leftArraySize + 1, rightArr.length);
|
||||||
|
|
||||||
|
return left + leftArraySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int partitionIterative(Integer[] arr, int left, int right) {
|
||||||
|
int pivot = arr[right], i = left;
|
||||||
|
for (int j = left; j <= right - 1; j++) {
|
||||||
|
if (arr[j] <= pivot) {
|
||||||
|
swap(arr, i, j);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
swap(arr, i, right);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int findKthElementByRandomizedQuickSelect(Integer[] arr, int left, int right, int k) {
|
||||||
|
if (k >= 0 && k <= right - left + 1) {
|
||||||
|
int pos = randomPartition(arr, left, right);
|
||||||
|
if (pos - left == k) {
|
||||||
|
return arr[pos];
|
||||||
|
}
|
||||||
|
if (pos - left > k) {
|
||||||
|
return findKthElementByRandomizedQuickSelect(arr, left, pos - 1, k);
|
||||||
|
}
|
||||||
|
return findKthElementByRandomizedQuickSelect(arr, pos + 1, right, k - pos + left - 1);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int randomPartition(Integer arr[], int left, int right) {
|
||||||
|
int n = right - left + 1;
|
||||||
|
int pivot = (int) (Math.random() * n);
|
||||||
|
swap(arr, left + pivot, right);
|
||||||
|
return partition(arr, left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void swap(Integer[] arr, int n1, int n2) {
|
||||||
|
int temp = arr[n2];
|
||||||
|
arr[n2] = arr[n1];
|
||||||
|
arr[n1] = temp;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,109 @@
|
||||||
|
package com.baeldung.algorithms.mcts.montecarlo;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.baeldung.algorithms.mcts.tictactoe.Board;
|
||||||
|
import com.baeldung.algorithms.mcts.tree.Node;
|
||||||
|
import com.baeldung.algorithms.mcts.tree.Tree;
|
||||||
|
|
||||||
|
public class MonteCarloTreeSearch {
|
||||||
|
|
||||||
|
private static final int WIN_SCORE = 10;
|
||||||
|
private int level;
|
||||||
|
private int opponent;
|
||||||
|
|
||||||
|
public MonteCarloTreeSearch() {
|
||||||
|
this.level = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLevel() {
|
||||||
|
return level;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLevel(int level) {
|
||||||
|
this.level = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getMillisForCurrentLevel() {
|
||||||
|
return 2 * (this.level - 1) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Board findNextMove(Board board, int playerNo) {
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
long end = start + 60 * getMillisForCurrentLevel();
|
||||||
|
|
||||||
|
opponent = 3 - playerNo;
|
||||||
|
Tree tree = new Tree();
|
||||||
|
Node rootNode = tree.getRoot();
|
||||||
|
rootNode.getState().setBoard(board);
|
||||||
|
rootNode.getState().setPlayerNo(opponent);
|
||||||
|
|
||||||
|
while (System.currentTimeMillis() < end) {
|
||||||
|
// Phase 1 - Selection
|
||||||
|
Node promisingNode = selectPromisingNode(rootNode);
|
||||||
|
// Phase 2 - Expansion
|
||||||
|
if (promisingNode.getState().getBoard().checkStatus() == Board.IN_PROGRESS)
|
||||||
|
expandNode(promisingNode);
|
||||||
|
|
||||||
|
// Phase 3 - Simulation
|
||||||
|
Node nodeToExplore = promisingNode;
|
||||||
|
if (promisingNode.getChildArray().size() > 0) {
|
||||||
|
nodeToExplore = promisingNode.getRandomChildNode();
|
||||||
|
}
|
||||||
|
int playoutResult = simulateRandomPlayout(nodeToExplore);
|
||||||
|
// Phase 4 - Update
|
||||||
|
backPropogation(nodeToExplore, playoutResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
Node winnerNode = rootNode.getChildWithMaxScore();
|
||||||
|
tree.setRoot(winnerNode);
|
||||||
|
return winnerNode.getState().getBoard();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node selectPromisingNode(Node rootNode) {
|
||||||
|
Node node = rootNode;
|
||||||
|
while (node.getChildArray().size() != 0) {
|
||||||
|
node = UCT.findBestNodeWithUCT(node);
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expandNode(Node node) {
|
||||||
|
List<State> possibleStates = node.getState().getAllPossibleStates();
|
||||||
|
possibleStates.forEach(state -> {
|
||||||
|
Node newNode = new Node(state);
|
||||||
|
newNode.setParent(node);
|
||||||
|
newNode.getState().setPlayerNo(node.getState().getOpponent());
|
||||||
|
node.getChildArray().add(newNode);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void backPropogation(Node nodeToExplore, int playerNo) {
|
||||||
|
Node tempNode = nodeToExplore;
|
||||||
|
while (tempNode != null) {
|
||||||
|
tempNode.getState().incrementVisit();
|
||||||
|
if (tempNode.getState().getPlayerNo() == playerNo)
|
||||||
|
tempNode.getState().addScore(WIN_SCORE);
|
||||||
|
tempNode = tempNode.getParent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int simulateRandomPlayout(Node node) {
|
||||||
|
Node tempNode = new Node(node);
|
||||||
|
State tempState = tempNode.getState();
|
||||||
|
int boardStatus = tempState.getBoard().checkStatus();
|
||||||
|
|
||||||
|
if (boardStatus == opponent) {
|
||||||
|
tempNode.getParent().getState().setWinScore(Integer.MIN_VALUE);
|
||||||
|
return boardStatus;
|
||||||
|
}
|
||||||
|
while (boardStatus == Board.IN_PROGRESS) {
|
||||||
|
tempState.togglePlayer();
|
||||||
|
tempState.randomPlay();
|
||||||
|
boardStatus = tempState.getBoard().checkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
return boardStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
package com.baeldung.algorithms.mcts.montecarlo;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.baeldung.algorithms.mcts.tictactoe.Board;
|
||||||
|
import com.baeldung.algorithms.mcts.tictactoe.Position;
|
||||||
|
|
||||||
|
public class State {
|
||||||
|
private Board board;
|
||||||
|
private int playerNo;
|
||||||
|
private int visitCount;
|
||||||
|
private double winScore;
|
||||||
|
|
||||||
|
public State() {
|
||||||
|
board = new Board();
|
||||||
|
}
|
||||||
|
|
||||||
|
public State(State state) {
|
||||||
|
this.board = new Board(state.getBoard());
|
||||||
|
this.playerNo = state.getPlayerNo();
|
||||||
|
this.visitCount = state.getVisitCount();
|
||||||
|
this.winScore = state.getWinScore();
|
||||||
|
}
|
||||||
|
|
||||||
|
public State(Board board) {
|
||||||
|
this.board = new Board(board);
|
||||||
|
}
|
||||||
|
|
||||||
|
Board getBoard() {
|
||||||
|
return board;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setBoard(Board board) {
|
||||||
|
this.board = board;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getPlayerNo() {
|
||||||
|
return playerNo;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPlayerNo(int playerNo) {
|
||||||
|
this.playerNo = playerNo;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getOpponent() {
|
||||||
|
return 3 - playerNo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getVisitCount() {
|
||||||
|
return visitCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVisitCount(int visitCount) {
|
||||||
|
this.visitCount = visitCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
double getWinScore() {
|
||||||
|
return winScore;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setWinScore(double winScore) {
|
||||||
|
this.winScore = winScore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<State> getAllPossibleStates() {
|
||||||
|
List<State> possibleStates = new ArrayList<>();
|
||||||
|
List<Position> availablePositions = this.board.getEmptyPositions();
|
||||||
|
availablePositions.forEach(p -> {
|
||||||
|
State newState = new State(this.board);
|
||||||
|
newState.setPlayerNo(3 - this.playerNo);
|
||||||
|
newState.getBoard().performMove(newState.getPlayerNo(), p);
|
||||||
|
possibleStates.add(newState);
|
||||||
|
});
|
||||||
|
return possibleStates;
|
||||||
|
}
|
||||||
|
|
||||||
|
void incrementVisit() {
|
||||||
|
this.visitCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addScore(double score) {
|
||||||
|
if (this.winScore != Integer.MIN_VALUE)
|
||||||
|
this.winScore += score;
|
||||||
|
}
|
||||||
|
|
||||||
|
void randomPlay() {
|
||||||
|
List<Position> availablePositions = this.board.getEmptyPositions();
|
||||||
|
int totalPossibilities = availablePositions.size();
|
||||||
|
int selectRandom = (int) (Math.random() * totalPossibilities);
|
||||||
|
this.board.performMove(this.playerNo, availablePositions.get(selectRandom));
|
||||||
|
}
|
||||||
|
|
||||||
|
void togglePlayer() {
|
||||||
|
this.playerNo = 3 - this.playerNo;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.baeldung.algorithms.mcts.montecarlo;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.baeldung.algorithms.mcts.tree.Node;
|
||||||
|
|
||||||
|
public class UCT {
|
||||||
|
|
||||||
|
public static double uctValue(int totalVisit, double nodeWinScore, int nodeVisit) {
|
||||||
|
if (nodeVisit == 0) {
|
||||||
|
return Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
|
return (nodeWinScore / (double) nodeVisit) + 1.41 * Math.sqrt(Math.log(totalVisit) / (double) nodeVisit);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Node findBestNodeWithUCT(Node node) {
|
||||||
|
int parentVisit = node.getState().getVisitCount();
|
||||||
|
return Collections.max(
|
||||||
|
node.getChildArray(),
|
||||||
|
Comparator.comparing(c -> uctValue(parentVisit, c.getState().getWinScore(), c.getState().getVisitCount())));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,155 @@
|
||||||
|
package com.baeldung.algorithms.mcts.tictactoe;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Board {
|
||||||
|
int[][] boardValues;
|
||||||
|
int totalMoves;
|
||||||
|
|
||||||
|
public static final int DEFAULT_BOARD_SIZE = 3;
|
||||||
|
|
||||||
|
public static final int IN_PROGRESS = -1;
|
||||||
|
public static final int DRAW = 0;
|
||||||
|
public static final int P1 = 1;
|
||||||
|
public static final int P2 = 2;
|
||||||
|
|
||||||
|
public Board() {
|
||||||
|
boardValues = new int[DEFAULT_BOARD_SIZE][DEFAULT_BOARD_SIZE];
|
||||||
|
}
|
||||||
|
|
||||||
|
public Board(int boardSize) {
|
||||||
|
boardValues = new int[boardSize][boardSize];
|
||||||
|
}
|
||||||
|
|
||||||
|
public Board(int[][] boardValues) {
|
||||||
|
this.boardValues = boardValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Board(int[][] boardValues, int totalMoves) {
|
||||||
|
this.boardValues = boardValues;
|
||||||
|
this.totalMoves = totalMoves;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Board(Board board) {
|
||||||
|
int boardLength = board.getBoardValues().length;
|
||||||
|
this.boardValues = new int[boardLength][boardLength];
|
||||||
|
int[][] boardValues = board.getBoardValues();
|
||||||
|
int n = boardValues.length;
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
int m = boardValues[i].length;
|
||||||
|
for (int j = 0; j < m; j++) {
|
||||||
|
this.boardValues[i][j] = boardValues[i][j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void performMove(int player, Position p) {
|
||||||
|
this.totalMoves++;
|
||||||
|
boardValues[p.getX()][p.getY()] = player;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[][] getBoardValues() {
|
||||||
|
return boardValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBoardValues(int[][] boardValues) {
|
||||||
|
this.boardValues = boardValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int checkStatus() {
|
||||||
|
int boardSize = boardValues.length;
|
||||||
|
int maxIndex = boardSize - 1;
|
||||||
|
int[] diag1 = new int[boardSize];
|
||||||
|
int[] diag2 = new int[boardSize];
|
||||||
|
|
||||||
|
for (int i = 0; i < boardSize; i++) {
|
||||||
|
int[] row = boardValues[i];
|
||||||
|
int[] col = new int[boardSize];
|
||||||
|
for (int j = 0; j < boardSize; j++) {
|
||||||
|
col[j] = boardValues[j][i];
|
||||||
|
}
|
||||||
|
|
||||||
|
int checkRowForWin = checkForWin(row);
|
||||||
|
if(checkRowForWin!=0)
|
||||||
|
return checkRowForWin;
|
||||||
|
|
||||||
|
int checkColForWin = checkForWin(col);
|
||||||
|
if(checkColForWin!=0)
|
||||||
|
return checkColForWin;
|
||||||
|
|
||||||
|
diag1[i] = boardValues[i][i];
|
||||||
|
diag2[i] = boardValues[maxIndex - i][i];
|
||||||
|
}
|
||||||
|
|
||||||
|
int checkDia1gForWin = checkForWin(diag1);
|
||||||
|
if(checkDia1gForWin!=0)
|
||||||
|
return checkDia1gForWin;
|
||||||
|
|
||||||
|
int checkDiag2ForWin = checkForWin(diag2);
|
||||||
|
if(checkDiag2ForWin!=0)
|
||||||
|
return checkDiag2ForWin;
|
||||||
|
|
||||||
|
if (getEmptyPositions().size() > 0)
|
||||||
|
return IN_PROGRESS;
|
||||||
|
else
|
||||||
|
return DRAW;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int checkForWin(int[] row) {
|
||||||
|
boolean isEqual = true;
|
||||||
|
int size = row.length;
|
||||||
|
int previous = row[0];
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
if (previous != row[i]) {
|
||||||
|
isEqual = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
previous = row[i];
|
||||||
|
}
|
||||||
|
if(isEqual)
|
||||||
|
return previous;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void printBoard() {
|
||||||
|
int size = this.boardValues.length;
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
for (int j = 0; j < size; j++) {
|
||||||
|
System.out.print(boardValues[i][j] + " ");
|
||||||
|
}
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Position> getEmptyPositions() {
|
||||||
|
int size = this.boardValues.length;
|
||||||
|
List<Position> emptyPositions = new ArrayList<>();
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
for (int j = 0; j < size; j++) {
|
||||||
|
if (boardValues[i][j] == 0)
|
||||||
|
emptyPositions.add(new Position(i, j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return emptyPositions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void printStatus() {
|
||||||
|
switch (this.checkStatus()) {
|
||||||
|
case P1:
|
||||||
|
System.out.println("Player 1 wins");
|
||||||
|
break;
|
||||||
|
case P2:
|
||||||
|
System.out.println("Player 2 wins");
|
||||||
|
break;
|
||||||
|
case DRAW:
|
||||||
|
System.out.println("Game Draw");
|
||||||
|
break;
|
||||||
|
case IN_PROGRESS:
|
||||||
|
System.out.println("Game In Progress");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package com.baeldung.algorithms.mcts.tictactoe;
|
||||||
|
|
||||||
|
public class Position {
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
|
||||||
|
public Position() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Position(int x, int y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getX() {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setX(int x) {
|
||||||
|
this.x = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getY() {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setY(int y) {
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
package com.baeldung.algorithms.mcts.tree;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.baeldung.algorithms.mcts.montecarlo.State;
|
||||||
|
|
||||||
|
public class Node {
|
||||||
|
State state;
|
||||||
|
Node parent;
|
||||||
|
List<Node> childArray;
|
||||||
|
|
||||||
|
public Node() {
|
||||||
|
this.state = new State();
|
||||||
|
childArray = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node(State state) {
|
||||||
|
this.state = state;
|
||||||
|
childArray = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node(State state, Node parent, List<Node> childArray) {
|
||||||
|
this.state = state;
|
||||||
|
this.parent = parent;
|
||||||
|
this.childArray = childArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node(Node node) {
|
||||||
|
this.childArray = new ArrayList<>();
|
||||||
|
this.state = new State(node.getState());
|
||||||
|
if (node.getParent() != null)
|
||||||
|
this.parent = node.getParent();
|
||||||
|
List<Node> childArray = node.getChildArray();
|
||||||
|
for (Node child : childArray) {
|
||||||
|
this.childArray.add(new Node(child));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public State getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setState(State state) {
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node getParent() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParent(Node parent) {
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Node> getChildArray() {
|
||||||
|
return childArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setChildArray(List<Node> childArray) {
|
||||||
|
this.childArray = childArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node getRandomChildNode() {
|
||||||
|
int noOfPossibleMoves = this.childArray.size();
|
||||||
|
int selectRandom = (int) (Math.random() * noOfPossibleMoves);
|
||||||
|
return this.childArray.get(selectRandom);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node getChildWithMaxScore() {
|
||||||
|
return Collections.max(this.childArray, Comparator.comparing(c -> {
|
||||||
|
return c.getState().getVisitCount();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package com.baeldung.algorithms.mcts.tree;
|
||||||
|
|
||||||
|
public class Tree {
|
||||||
|
Node root;
|
||||||
|
|
||||||
|
public Tree() {
|
||||||
|
root = new Node();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tree(Node root) {
|
||||||
|
this.root = root;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node getRoot() {
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRoot(Node root) {
|
||||||
|
this.root = root;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addChild(Node parent, Node child) {
|
||||||
|
parent.getChildArray().add(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package com.baeldung.algorithms.minimax;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
|
class GameOfBones {
|
||||||
|
static List<Integer> getPossibleStates(int noOfBonesInHeap) {
|
||||||
|
return IntStream.rangeClosed(1, 3).boxed()
|
||||||
|
.map(i -> noOfBonesInHeap - i)
|
||||||
|
.filter(newHeapCount -> newHeapCount >= 0)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
package com.baeldung.algorithms.minimax;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
public class MiniMax {
|
||||||
|
private Tree tree;
|
||||||
|
|
||||||
|
public Tree getTree() {
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void constructTree(int noOfBones) {
|
||||||
|
tree = new Tree();
|
||||||
|
Node root = new Node(noOfBones, true);
|
||||||
|
tree.setRoot(root);
|
||||||
|
constructTree(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void constructTree(Node parentNode) {
|
||||||
|
List<Integer> listofPossibleHeaps = GameOfBones.getPossibleStates(parentNode.getNoOfBones());
|
||||||
|
boolean isChildMaxPlayer = !parentNode.isMaxPlayer();
|
||||||
|
listofPossibleHeaps.forEach(n -> {
|
||||||
|
Node newNode = new Node(n, isChildMaxPlayer);
|
||||||
|
parentNode.addChild(newNode);
|
||||||
|
if (newNode.getNoOfBones() > 0) {
|
||||||
|
constructTree(newNode);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean checkWin() {
|
||||||
|
Node root = tree.getRoot();
|
||||||
|
checkWin(root);
|
||||||
|
return root.getScore() == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkWin(Node node) {
|
||||||
|
List<Node> children = node.getChildren();
|
||||||
|
boolean isMaxPlayer = node.isMaxPlayer();
|
||||||
|
children.forEach(child -> {
|
||||||
|
if (child.getNoOfBones() == 0) {
|
||||||
|
child.setScore(isMaxPlayer ? 1 : -1);
|
||||||
|
} else {
|
||||||
|
checkWin(child);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Node bestChild = findBestChild(isMaxPlayer, children);
|
||||||
|
node.setScore(bestChild.getScore());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node findBestChild(boolean isMaxPlayer, List<Node> children) {
|
||||||
|
Comparator<Node> byScoreComparator = Comparator.comparing(Node::getScore);
|
||||||
|
|
||||||
|
return children.stream()
|
||||||
|
.max(isMaxPlayer ? byScoreComparator : byScoreComparator.reversed())
|
||||||
|
.orElseThrow(NoSuchElementException::new);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package com.baeldung.algorithms.minimax;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Node {
|
||||||
|
private int noOfBones;
|
||||||
|
private boolean isMaxPlayer;
|
||||||
|
private int score;
|
||||||
|
private List<Node> children;
|
||||||
|
|
||||||
|
public Node(int noOfBones, boolean isMaxPlayer) {
|
||||||
|
this.noOfBones = noOfBones;
|
||||||
|
this.isMaxPlayer = isMaxPlayer;
|
||||||
|
children = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
int getNoOfBones() {
|
||||||
|
return noOfBones;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isMaxPlayer() {
|
||||||
|
return isMaxPlayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getScore() {
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setScore(int score) {
|
||||||
|
this.score = score;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Node> getChildren() {
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addChild(Node newNode) {
|
||||||
|
children.add(newNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.baeldung.algorithms.minimax;
|
||||||
|
|
||||||
|
public class Tree {
|
||||||
|
private Node root;
|
||||||
|
|
||||||
|
Tree() {
|
||||||
|
}
|
||||||
|
|
||||||
|
Node getRoot() {
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setRoot(Node root) {
|
||||||
|
this.root = root;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<configuration>
|
||||||
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<encoder>
|
||||||
|
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
|
||||||
|
</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<root level="INFO">
|
||||||
|
<appender-ref ref="STDOUT" />
|
||||||
|
</root>
|
||||||
|
</configuration>
|
|
@ -0,0 +1,58 @@
|
||||||
|
package com.baeldung.algorithms;
|
||||||
|
|
||||||
|
import com.baeldung.algorithms.hillclimbing.HillClimbing;
|
||||||
|
import com.baeldung.algorithms.hillclimbing.State;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
public class HillClimbingAlgorithmUnitTest {
|
||||||
|
private Stack<String> initStack;
|
||||||
|
private Stack<String> goalStack;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void initStacks() {
|
||||||
|
String blockArr[] = { "B", "C", "D", "A" };
|
||||||
|
String goalBlockArr[] = { "A", "B", "C", "D" };
|
||||||
|
initStack = new Stack<>();
|
||||||
|
for (String block : blockArr)
|
||||||
|
initStack.push(block);
|
||||||
|
goalStack = new Stack<>();
|
||||||
|
for (String block : goalBlockArr)
|
||||||
|
goalStack.push(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenInitAndGoalState_whenGetPathWithHillClimbing_thenPathFound() {
|
||||||
|
HillClimbing hillClimbing = new HillClimbing();
|
||||||
|
|
||||||
|
List<State> path;
|
||||||
|
try {
|
||||||
|
path = hillClimbing.getRouteWithHillClimbing(initStack, goalStack);
|
||||||
|
assertNotNull(path);
|
||||||
|
assertEquals(path.get(path.size() - 1)
|
||||||
|
.getState()
|
||||||
|
.get(0), goalStack);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenCurrentState_whenFindNextState_thenBetterHeuristics() {
|
||||||
|
HillClimbing hillClimbing = new HillClimbing();
|
||||||
|
List<Stack<String>> initList = new ArrayList<>();
|
||||||
|
initList.add(initStack);
|
||||||
|
State currentState = new State(initList);
|
||||||
|
currentState.setHeuristics(hillClimbing.getHeuristicsValue(initList, goalStack));
|
||||||
|
State nextState = hillClimbing.findNextState(currentState, goalStack);
|
||||||
|
assertTrue(nextState.getHeuristics() > currentState.getHeuristics());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
package com.baeldung.algorithms;
|
||||||
|
|
||||||
|
import com.baeldung.algorithms.automata.*;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
public final class RtFiniteStateMachineLongRunningUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void acceptsSimplePair() {
|
||||||
|
String json = "{\"key\":\"value\"}";
|
||||||
|
FiniteStateMachine machine = this.buildJsonStateMachine();
|
||||||
|
for (int i = 0; i < json.length(); i++) {
|
||||||
|
machine = machine.switchState(String.valueOf(json.charAt(i)));
|
||||||
|
}
|
||||||
|
assertTrue(machine.canStop());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void acceptsMorePairs() {
|
||||||
|
String json = "{\"key1\":\"value1\",\"key2\":\"value2\"}";
|
||||||
|
FiniteStateMachine machine = this.buildJsonStateMachine();
|
||||||
|
for (int i = 0; i < json.length(); i++) {
|
||||||
|
machine = machine.switchState(String.valueOf(json.charAt(i)));
|
||||||
|
}
|
||||||
|
assertTrue(machine.canStop());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void missingColon() {
|
||||||
|
String json = "{\"key\"\"value\"}";
|
||||||
|
FiniteStateMachine machine = this.buildJsonStateMachine();
|
||||||
|
for (int i = 0; i < json.length(); i++) {
|
||||||
|
machine = machine.switchState(String.valueOf(json.charAt(i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a finite state machine to validate a simple
|
||||||
|
* Json object.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private FiniteStateMachine buildJsonStateMachine() {
|
||||||
|
State first = new RtState();
|
||||||
|
State second = new RtState();
|
||||||
|
State third = new RtState();
|
||||||
|
State fourth = new RtState();
|
||||||
|
State fifth = new RtState();
|
||||||
|
State sixth = new RtState();
|
||||||
|
State seventh = new RtState();
|
||||||
|
State eighth = new RtState(true);
|
||||||
|
|
||||||
|
first.with(new RtTransition("{", second));
|
||||||
|
second.with(new RtTransition("\"", third));
|
||||||
|
//Add transitions with chars 0-9 and a-z
|
||||||
|
for (int i = 0; i < 26; i++) {
|
||||||
|
if (i < 10) {
|
||||||
|
third = third.with(new RtTransition(String.valueOf(i), third));
|
||||||
|
sixth = sixth.with(new RtTransition(String.valueOf(i), sixth));
|
||||||
|
}
|
||||||
|
third = third.with(new RtTransition(String.valueOf((char) ('a' + i)), third));
|
||||||
|
sixth = sixth.with(new RtTransition(String.valueOf((char) ('a' + i)), sixth));
|
||||||
|
}
|
||||||
|
third.with(new RtTransition("\"", fourth));
|
||||||
|
fourth.with(new RtTransition(":", fifth));
|
||||||
|
fifth.with(new RtTransition("\"", sixth));
|
||||||
|
sixth.with(new RtTransition("\"", seventh));
|
||||||
|
seventh.with(new RtTransition(",", second));
|
||||||
|
seventh.with(new RtTransition("}", eighth));
|
||||||
|
return new RtFiniteStateMachine(first);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
package com.baeldung.algorithms.binarysearch;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import com.baeldung.algorithms.binarysearch.BinarySearch;
|
||||||
|
|
||||||
|
public class BinarySearchUnitTest {
|
||||||
|
|
||||||
|
int[] sortedArray = { 0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 9 };
|
||||||
|
int key = 6;
|
||||||
|
int expectedIndexForSearchKey = 7;
|
||||||
|
int low = 0;
|
||||||
|
int high = sortedArray.length - 1;
|
||||||
|
List<Integer> sortedList = Arrays.asList(0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 9);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenASortedArrayOfIntegers_whenBinarySearchRunIterativelyForANumber_thenGetIndexOfTheNumber() {
|
||||||
|
BinarySearch binSearch = new BinarySearch();
|
||||||
|
Assert.assertEquals(expectedIndexForSearchKey, binSearch.runBinarySearchIteratively(sortedArray, key, low, high));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenASortedArrayOfIntegers_whenBinarySearchRunRecursivelyForANumber_thenGetIndexOfTheNumber() {
|
||||||
|
BinarySearch binSearch = new BinarySearch();
|
||||||
|
Assert.assertEquals(expectedIndexForSearchKey, binSearch.runBinarySearchRecursively(sortedArray, key, low, high));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenASortedArrayOfIntegers_whenBinarySearchRunUsingArraysClassStaticMethodForANumber_thenGetIndexOfTheNumber() {
|
||||||
|
BinarySearch binSearch = new BinarySearch();
|
||||||
|
Assert.assertEquals(expectedIndexForSearchKey, binSearch.runBinarySearchUsingJavaArrays(sortedArray, key));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenASortedListOfIntegers_whenBinarySearchRunUsingCollectionsClassStaticMethodForANumber_thenGetIndexOfTheNumber() {
|
||||||
|
BinarySearch binSearch = new BinarySearch();
|
||||||
|
Assert.assertEquals(expectedIndexForSearchKey, binSearch.runBinarySearchUsingJavaCollections(sortedList, key));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package com.baeldung.algorithms.kthlargest;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.*;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class FindKthLargestUnitTest {
|
||||||
|
|
||||||
|
private FindKthLargest findKthLargest;
|
||||||
|
private Integer[] arr = { 3, 7, 1, 2, 8, 10, 4, 5, 6, 9 };
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
findKthLargest = new FindKthLargest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenIntArray_whenFindKthLargestBySorting_thenGetResult() {
|
||||||
|
int k = 3;
|
||||||
|
assertThat(findKthLargest.findKthLargestBySorting(arr, k)).isEqualTo(8);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenIntArray_whenFindKthLargestBySortingDesc_thenGetResult() {
|
||||||
|
int k = 3;
|
||||||
|
assertThat(findKthLargest.findKthLargestBySortingDesc(arr, k)).isEqualTo(8);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenIntArray_whenFindKthLargestByQuickSelect_thenGetResult() {
|
||||||
|
int k = 3;
|
||||||
|
int kthLargest = arr.length - k;
|
||||||
|
assertThat(findKthLargest.findKthElementByQuickSelect(arr, 0, arr.length - 1, kthLargest)).isEqualTo(8);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenIntArray_whenFindKthElementByQuickSelectIterative_thenGetResult() {
|
||||||
|
int k = 3;
|
||||||
|
int kthLargest = arr.length - k;
|
||||||
|
assertThat(findKthLargest.findKthElementByQuickSelectWithIterativePartition(arr, 0, arr.length - 1, kthLargest)).isEqualTo(8);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenIntArray_whenFindKthSmallestByQuickSelect_thenGetResult() {
|
||||||
|
int k = 3;
|
||||||
|
assertThat(findKthLargest.findKthElementByQuickSelect(arr, 0, arr.length - 1, k - 1)).isEqualTo(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenIntArray_whenFindKthLargestByRandomizedQuickSelect_thenGetResult() {
|
||||||
|
int k = 3;
|
||||||
|
int kthLargest = arr.length - k;
|
||||||
|
assertThat(findKthLargest.findKthElementByRandomizedQuickSelect(arr, 0, arr.length - 1, kthLargest)).isEqualTo(8);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
package com.baeldung.algorithms.mcts;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import com.baeldung.algorithms.mcts.montecarlo.MonteCarloTreeSearch;
|
||||||
|
import com.baeldung.algorithms.mcts.montecarlo.State;
|
||||||
|
import com.baeldung.algorithms.mcts.montecarlo.UCT;
|
||||||
|
import com.baeldung.algorithms.mcts.tictactoe.Board;
|
||||||
|
import com.baeldung.algorithms.mcts.tictactoe.Position;
|
||||||
|
import com.baeldung.algorithms.mcts.tree.Tree;
|
||||||
|
|
||||||
|
public class MCTSUnitTest {
|
||||||
|
private Tree gameTree;
|
||||||
|
private MonteCarloTreeSearch mcts;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void initGameTree() {
|
||||||
|
gameTree = new Tree();
|
||||||
|
mcts = new MonteCarloTreeSearch();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenStats_whenGetUCTForNode_thenUCTMatchesWithManualData() {
|
||||||
|
double uctValue = 15.79;
|
||||||
|
assertEquals(UCT.uctValue(600, 300, 20), uctValue, 0.01);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void giveninitBoardState_whenGetAllPossibleStates_thenNonEmptyList() {
|
||||||
|
State initState = gameTree.getRoot().getState();
|
||||||
|
List<State> possibleStates = initState.getAllPossibleStates();
|
||||||
|
assertTrue(possibleStates.size() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenEmptyBoard_whenPerformMove_thenLessAvailablePossitions() {
|
||||||
|
Board board = new Board();
|
||||||
|
int initAvailablePositions = board.getEmptyPositions().size();
|
||||||
|
board.performMove(Board.P1, new Position(1, 1));
|
||||||
|
int availablePositions = board.getEmptyPositions().size();
|
||||||
|
assertTrue(initAvailablePositions > availablePositions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenEmptyBoard_whenSimulateInterAIPlay_thenGameDraw() {
|
||||||
|
Board board = new Board();
|
||||||
|
|
||||||
|
int player = Board.P1;
|
||||||
|
int totalMoves = Board.DEFAULT_BOARD_SIZE * Board.DEFAULT_BOARD_SIZE;
|
||||||
|
for (int i = 0; i < totalMoves; i++) {
|
||||||
|
board = mcts.findNextMove(board, player);
|
||||||
|
if (board.checkStatus() != -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
player = 3 - player;
|
||||||
|
}
|
||||||
|
int winStatus = board.checkStatus();
|
||||||
|
assertEquals(winStatus, Board.DRAW);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenEmptyBoard_whenLevel1VsLevel3_thenLevel3WinsOrDraw() {
|
||||||
|
Board board = new Board();
|
||||||
|
MonteCarloTreeSearch mcts1 = new MonteCarloTreeSearch();
|
||||||
|
mcts1.setLevel(1);
|
||||||
|
MonteCarloTreeSearch mcts3 = new MonteCarloTreeSearch();
|
||||||
|
mcts3.setLevel(3);
|
||||||
|
|
||||||
|
int player = Board.P1;
|
||||||
|
int totalMoves = Board.DEFAULT_BOARD_SIZE * Board.DEFAULT_BOARD_SIZE;
|
||||||
|
for (int i = 0; i < totalMoves; i++) {
|
||||||
|
if (player == Board.P1)
|
||||||
|
board = mcts3.findNextMove(board, player);
|
||||||
|
else
|
||||||
|
board = mcts1.findNextMove(board, player);
|
||||||
|
|
||||||
|
if (board.checkStatus() != -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
player = 3 - player;
|
||||||
|
}
|
||||||
|
int winStatus = board.checkStatus();
|
||||||
|
assertTrue(winStatus == Board.DRAW || winStatus == Board.P1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package com.baeldung.algorithms.minimax;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import com.baeldung.algorithms.minimax.MiniMax;
|
||||||
|
import com.baeldung.algorithms.minimax.Tree;
|
||||||
|
|
||||||
|
public class MinimaxUnitTest {
|
||||||
|
private Tree gameTree;
|
||||||
|
private MiniMax miniMax;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void initMiniMaxUtility() {
|
||||||
|
miniMax = new MiniMax();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenMiniMax_whenConstructTree_thenNotNullTree() {
|
||||||
|
assertNull(gameTree);
|
||||||
|
miniMax.constructTree(6);
|
||||||
|
gameTree = miniMax.getTree();
|
||||||
|
assertNotNull(gameTree);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenMiniMax_whenCheckWin_thenComputeOptimal() {
|
||||||
|
miniMax.constructTree(6);
|
||||||
|
boolean result = miniMax.checkWin();
|
||||||
|
assertTrue(result);
|
||||||
|
miniMax.constructTree(8);
|
||||||
|
result = miniMax.checkWin();
|
||||||
|
assertFalse(result);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
## Algorithms - Miscellaneous
|
||||||
|
|
||||||
|
This module contains articles about algorithms. Some classes of algorithms, e.g., [sorting](/../algorithms-sorting) and
|
||||||
|
[genetic algorithms](/../algorithms-genetic), have their own dedicated modules.
|
||||||
|
|
||||||
|
### Relevant articles:
|
||||||
|
|
||||||
|
- [Dijkstra Shortest Path Algorithm in Java](https://www.baeldung.com/java-dijkstra)
|
||||||
|
- [Introduction to Cobertura](https://www.baeldung.com/cobertura)
|
||||||
|
- [Test a Linked List for Cyclicity](https://www.baeldung.com/java-linked-list-cyclicity)
|
||||||
|
- [Introduction to JGraphT](https://www.baeldung.com/jgrapht)
|
||||||
|
- [A Maze Solver in Java](https://www.baeldung.com/java-solve-maze)
|
||||||
|
- [Create a Sudoku Solver in Java](https://www.baeldung.com/java-sudoku)
|
||||||
|
- [Displaying Money Amounts in Words](https://www.baeldung.com/java-money-into-words)
|
||||||
|
- [A Collaborative Filtering Recommendation System in Java](https://www.baeldung.com/java-collaborative-filtering-recommendations)
|
||||||
|
- More articles: [[<-- prev]](/../algorithms-miscellaneous-1) [[next -->]](/../algorithms-miscellaneous-3)
|
|
@ -0,0 +1,96 @@
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>algorithms-miscellaneous-2</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<name>algorithms-miscellaneous-2</name>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>com.baeldung</groupId>
|
||||||
|
<artifactId>parent-modules</artifactId>
|
||||||
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-math3</artifactId>
|
||||||
|
<version>${commons-math3.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-codec</groupId>
|
||||||
|
<artifactId>commons-codec</artifactId>
|
||||||
|
<version>${commons-codec.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>${lombok.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jgrapht</groupId>
|
||||||
|
<artifactId>jgrapht-core</artifactId>
|
||||||
|
<version>${org.jgrapht.core.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jgrapht</groupId>
|
||||||
|
<artifactId>jgrapht-ext</artifactId>
|
||||||
|
<version>${org.jgrapht.ext.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>pl.allegro.finance</groupId>
|
||||||
|
<artifactId>tradukisto</artifactId>
|
||||||
|
<version>${tradukisto.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.assertj</groupId>
|
||||||
|
<artifactId>assertj-core</artifactId>
|
||||||
|
<version>${org.assertj.core.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<pluginManagement>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
|
<artifactId>exec-maven-plugin</artifactId>
|
||||||
|
<version>${exec-maven-plugin.version}</version>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</pluginManagement>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<reporting>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
|
<artifactId>cobertura-maven-plugin</artifactId>
|
||||||
|
<version>${cobertura-maven-plugin.version}</version>
|
||||||
|
<configuration>
|
||||||
|
<instrumentation>
|
||||||
|
<ignores>
|
||||||
|
<ignore>com/baeldung/algorithms/dijkstra/*</ignore>
|
||||||
|
</ignores>
|
||||||
|
<excludes>
|
||||||
|
<exclude>com/baeldung/algorithms/dijkstra/*</exclude>
|
||||||
|
</excludes>
|
||||||
|
</instrumentation>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</reporting>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<commons-math3.version>3.6.1</commons-math3.version>
|
||||||
|
<tradukisto.version>1.0.1</tradukisto.version>
|
||||||
|
<org.jgrapht.core.version>1.0.1</org.jgrapht.core.version>
|
||||||
|
<org.jgrapht.ext.version>1.0.1</org.jgrapht.ext.version>
|
||||||
|
<org.assertj.core.version>3.9.0</org.assertj.core.version>
|
||||||
|
<commons-codec.version>1.11</commons-codec.version>
|
||||||
|
<cobertura-maven-plugin.version>2.7</cobertura-maven-plugin.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,28 @@
|
||||||
|
package com.baeldung.algorithms;
|
||||||
|
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
|
import com.baeldung.algorithms.slope_one.SlopeOne;
|
||||||
|
|
||||||
|
public class RunAlgorithm {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
|
||||||
|
Scanner in = new Scanner(System.in);
|
||||||
|
System.out.println("1 - Slope One");
|
||||||
|
System.out.println("2 - Dijkstra");
|
||||||
|
int decision = in.nextInt();
|
||||||
|
switch (decision) {
|
||||||
|
case 1:
|
||||||
|
SlopeOne.slopeOne(3);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
System.out.println("Please run the DijkstraAlgorithmLongRunningUnitTest.");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
System.out.println("Unknown option");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
in.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.baeldung.algorithms.editdistance;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public class EditDistanceBase {
|
||||||
|
|
||||||
|
static int costOfSubstitution(char a, char b) {
|
||||||
|
return a == b ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int min(int... numbers) {
|
||||||
|
return Arrays.stream(numbers)
|
||||||
|
.min().orElse(Integer.MAX_VALUE);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package com.baeldung.algorithms.editdistance;
|
||||||
|
|
||||||
|
public class EditDistanceDynamicProgramming extends EditDistanceBase {
|
||||||
|
|
||||||
|
static int calculate(String x, String y) {
|
||||||
|
int[][] dp = new int[x.length() + 1][y.length() + 1];
|
||||||
|
|
||||||
|
for (int i = 0; i <= x.length(); i++) {
|
||||||
|
for (int j = 0; j <= y.length(); j++) {
|
||||||
|
if (i == 0)
|
||||||
|
dp[i][j] = j;
|
||||||
|
|
||||||
|
else if (j == 0)
|
||||||
|
dp[i][j] = i;
|
||||||
|
|
||||||
|
else {
|
||||||
|
dp[i][j] = min(dp[i - 1][j - 1]
|
||||||
|
+ costOfSubstitution(x.charAt(i - 1), y.charAt(j - 1)),
|
||||||
|
dp[i - 1][j] + 1, dp[i][j - 1] + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dp[x.length()][y.length()];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package com.baeldung.algorithms.editdistance;
|
||||||
|
|
||||||
|
public class EditDistanceRecursive extends EditDistanceBase {
|
||||||
|
|
||||||
|
static int calculate(String x, String y) {
|
||||||
|
|
||||||
|
if (x.isEmpty()) {
|
||||||
|
return y.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y.isEmpty()) {
|
||||||
|
return x.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
int substitution = calculate(x.substring(1), y.substring(1)) + costOfSubstitution(x.charAt(0), y.charAt(0));
|
||||||
|
int insertion = calculate(x, y.substring(1)) + 1;
|
||||||
|
int deletion = calculate(x.substring(1), y) + 1;
|
||||||
|
|
||||||
|
return min(substitution, insertion, deletion);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package com.baeldung.algorithms.ga.dijkstra;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class Dijkstra {
|
||||||
|
|
||||||
|
public static Graph calculateShortestPathFromSource(Graph graph, Node source) {
|
||||||
|
|
||||||
|
source.setDistance(0);
|
||||||
|
|
||||||
|
Set<Node> settledNodes = new HashSet<>();
|
||||||
|
Set<Node> unsettledNodes = new HashSet<>();
|
||||||
|
unsettledNodes.add(source);
|
||||||
|
|
||||||
|
while (unsettledNodes.size() != 0) {
|
||||||
|
Node currentNode = getLowestDistanceNode(unsettledNodes);
|
||||||
|
unsettledNodes.remove(currentNode);
|
||||||
|
for (Entry<Node, Integer> adjacencyPair : currentNode.getAdjacentNodes().entrySet()) {
|
||||||
|
Node adjacentNode = adjacencyPair.getKey();
|
||||||
|
Integer edgeWeigh = adjacencyPair.getValue();
|
||||||
|
|
||||||
|
if (!settledNodes.contains(adjacentNode)) {
|
||||||
|
CalculateMinimumDistance(adjacentNode, edgeWeigh, currentNode);
|
||||||
|
unsettledNodes.add(adjacentNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
settledNodes.add(currentNode);
|
||||||
|
}
|
||||||
|
return graph;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CalculateMinimumDistance(Node evaluationNode, Integer edgeWeigh, Node sourceNode) {
|
||||||
|
Integer sourceDistance = sourceNode.getDistance();
|
||||||
|
if (sourceDistance + edgeWeigh < evaluationNode.getDistance()) {
|
||||||
|
evaluationNode.setDistance(sourceDistance + edgeWeigh);
|
||||||
|
LinkedList<Node> shortestPath = new LinkedList<>(sourceNode.getShortestPath());
|
||||||
|
shortestPath.add(sourceNode);
|
||||||
|
evaluationNode.setShortestPath(shortestPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Node getLowestDistanceNode(Set<Node> unsettledNodes) {
|
||||||
|
Node lowestDistanceNode = null;
|
||||||
|
int lowestDistance = Integer.MAX_VALUE;
|
||||||
|
for (Node node : unsettledNodes) {
|
||||||
|
int nodeDistance = node.getDistance();
|
||||||
|
if (nodeDistance < lowestDistance) {
|
||||||
|
lowestDistance = nodeDistance;
|
||||||
|
lowestDistanceNode = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lowestDistanceNode;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package com.baeldung.algorithms.ga.dijkstra;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class Graph {
|
||||||
|
|
||||||
|
private Set<Node> nodes = new HashSet<>();
|
||||||
|
|
||||||
|
public void addNode(Node nodeA) {
|
||||||
|
nodes.add(nodeA);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Node> getNodes() {
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNodes(Set<Node> nodes) {
|
||||||
|
this.nodes = nodes;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
package com.baeldung.algorithms.ga.dijkstra;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class Node {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private LinkedList<Node> shortestPath = new LinkedList<>();
|
||||||
|
|
||||||
|
private Integer distance = Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
private Map<Node, Integer> adjacentNodes = new HashMap<>();
|
||||||
|
|
||||||
|
public Node(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addDestination(Node destination, int distance) {
|
||||||
|
adjacentNodes.put(destination, distance);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Node, Integer> getAdjacentNodes() {
|
||||||
|
return adjacentNodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAdjacentNodes(Map<Node, Integer> adjacentNodes) {
|
||||||
|
this.adjacentNodes = adjacentNodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getDistance() {
|
||||||
|
return distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDistance(Integer distance) {
|
||||||
|
this.distance = distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Node> getShortestPath() {
|
||||||
|
return shortestPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShortestPath(LinkedList<Node> shortestPath) {
|
||||||
|
this.shortestPath = shortestPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package com.baeldung.algorithms.linkedlist;
|
||||||
|
|
||||||
|
public class CycleDetectionBruteForce {
|
||||||
|
|
||||||
|
public static <T> CycleDetectionResult<T> detectCycle(Node<T> head) {
|
||||||
|
if (head == null) {
|
||||||
|
return new CycleDetectionResult<>(false, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
Node<T> it1 = head;
|
||||||
|
int nodesTraversedByOuter = 0;
|
||||||
|
while (it1 != null && it1.next != null) {
|
||||||
|
it1 = it1.next;
|
||||||
|
nodesTraversedByOuter++;
|
||||||
|
|
||||||
|
int x = nodesTraversedByOuter;
|
||||||
|
Node<T> it2 = head;
|
||||||
|
int noOfTimesCurrentNodeVisited = 0;
|
||||||
|
|
||||||
|
while (x > 0) {
|
||||||
|
it2 = it2.next;
|
||||||
|
|
||||||
|
if (it2 == it1) {
|
||||||
|
noOfTimesCurrentNodeVisited++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (noOfTimesCurrentNodeVisited == 2) {
|
||||||
|
return new CycleDetectionResult<>(true, it1);
|
||||||
|
}
|
||||||
|
|
||||||
|
x--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CycleDetectionResult<>(false, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package com.baeldung.algorithms.linkedlist;
|
||||||
|
|
||||||
|
public class CycleDetectionByFastAndSlowIterators {
|
||||||
|
|
||||||
|
public static <T> CycleDetectionResult<T> detectCycle(Node<T> head) {
|
||||||
|
if (head == null) {
|
||||||
|
return new CycleDetectionResult<>(false, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
Node<T> slow = head;
|
||||||
|
Node<T> fast = head;
|
||||||
|
|
||||||
|
while (fast != null && fast.next != null) {
|
||||||
|
slow = slow.next;
|
||||||
|
fast = fast.next.next;
|
||||||
|
|
||||||
|
if (slow == fast) {
|
||||||
|
return new CycleDetectionResult<>(true, fast);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CycleDetectionResult<>(false, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package com.baeldung.algorithms.linkedlist;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class CycleDetectionByHashing {
|
||||||
|
|
||||||
|
public static <T> CycleDetectionResult<T> detectCycle(Node<T> head) {
|
||||||
|
if (head == null) {
|
||||||
|
return new CycleDetectionResult<>(false, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<Node<T>> set = new HashSet<>();
|
||||||
|
Node<T> node = head;
|
||||||
|
|
||||||
|
while (node != null) {
|
||||||
|
if (set.contains(node)) {
|
||||||
|
return new CycleDetectionResult<>(true, node);
|
||||||
|
}
|
||||||
|
set.add(node);
|
||||||
|
node = node.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CycleDetectionResult<>(false, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package com.baeldung.algorithms.linkedlist;
|
||||||
|
|
||||||
|
public class CycleDetectionResult<T> {
|
||||||
|
boolean cycleExists;
|
||||||
|
Node<T> node;
|
||||||
|
|
||||||
|
public CycleDetectionResult(boolean cycleExists, Node<T> node) {
|
||||||
|
super();
|
||||||
|
this.cycleExists = cycleExists;
|
||||||
|
this.node = node;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
package com.baeldung.algorithms.linkedlist;
|
||||||
|
|
||||||
|
public class CycleRemovalBruteForce {
|
||||||
|
|
||||||
|
public static <T> boolean detectAndRemoveCycle(Node<T> head) {
|
||||||
|
CycleDetectionResult<T> result = CycleDetectionByFastAndSlowIterators.detectCycle(head);
|
||||||
|
|
||||||
|
if (result.cycleExists) {
|
||||||
|
removeCycle(result.node, head);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.cycleExists;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param loopNodeParam - reference to the node where Flyods cycle
|
||||||
|
* finding algorithm ends, i.e. the fast and the slow iterators
|
||||||
|
* meet.
|
||||||
|
* @param head - reference to the head of the list
|
||||||
|
*/
|
||||||
|
private static <T> void removeCycle(Node<T> loopNodeParam, Node<T> head) {
|
||||||
|
Node<T> it = head;
|
||||||
|
|
||||||
|
while (it != null) {
|
||||||
|
if (isNodeReachableFromLoopNode(it, loopNodeParam)) {
|
||||||
|
Node<T> loopStart = it;
|
||||||
|
findEndNodeAndBreakCycle(loopStart);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
it = it.next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> boolean isNodeReachableFromLoopNode(Node<T> it, Node<T> loopNodeParam) {
|
||||||
|
Node<T> loopNode = loopNodeParam;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (it == loopNode) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
loopNode = loopNode.next;
|
||||||
|
} while (loopNode.next != loopNodeParam);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> void findEndNodeAndBreakCycle(Node<T> loopStartParam) {
|
||||||
|
Node<T> loopStart = loopStartParam;
|
||||||
|
|
||||||
|
while (loopStart.next != loopStartParam) {
|
||||||
|
loopStart = loopStart.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
loopStart.next = null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
package com.baeldung.algorithms.linkedlist;
|
||||||
|
|
||||||
|
public class CycleRemovalByCountingLoopNodes {
|
||||||
|
|
||||||
|
public static <T> boolean detectAndRemoveCycle(Node<T> head) {
|
||||||
|
CycleDetectionResult<T> result = CycleDetectionByFastAndSlowIterators.detectCycle(head);
|
||||||
|
|
||||||
|
if (result.cycleExists) {
|
||||||
|
removeCycle(result.node, head);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.cycleExists;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> void removeCycle(Node<T> loopNodeParam, Node<T> head) {
|
||||||
|
int cycleLength = calculateCycleLength(loopNodeParam);
|
||||||
|
Node<T> cycleLengthAdvancedIterator = head;
|
||||||
|
Node<T> it = head;
|
||||||
|
|
||||||
|
for (int i = 0; i < cycleLength; i++) {
|
||||||
|
cycleLengthAdvancedIterator = cycleLengthAdvancedIterator.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (it.next != cycleLengthAdvancedIterator.next) {
|
||||||
|
it = it.next;
|
||||||
|
cycleLengthAdvancedIterator = cycleLengthAdvancedIterator.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
cycleLengthAdvancedIterator.next = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> int calculateCycleLength(Node<T> loopNodeParam) {
|
||||||
|
Node<T> loopNode = loopNodeParam;
|
||||||
|
int length = 1;
|
||||||
|
|
||||||
|
while (loopNode.next != loopNodeParam) {
|
||||||
|
length++;
|
||||||
|
loopNode = loopNode.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package com.baeldung.algorithms.linkedlist;
|
||||||
|
|
||||||
|
public class CycleRemovalWithoutCountingLoopNodes {
|
||||||
|
|
||||||
|
public static <T> boolean detectAndRemoveCycle(Node<T> head) {
|
||||||
|
CycleDetectionResult<T> result = CycleDetectionByFastAndSlowIterators.detectCycle(head);
|
||||||
|
|
||||||
|
if (result.cycleExists) {
|
||||||
|
removeCycle(result.node, head);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.cycleExists;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> void removeCycle(Node<T> meetingPointParam, Node<T> head) {
|
||||||
|
Node<T> loopNode = meetingPointParam;
|
||||||
|
Node<T> it = head;
|
||||||
|
|
||||||
|
while (loopNode.next != it.next) {
|
||||||
|
it = it.next;
|
||||||
|
loopNode = loopNode.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
loopNode.next = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package com.baeldung.algorithms.linkedlist;
|
||||||
|
|
||||||
|
public class Node<T> {
|
||||||
|
T data;
|
||||||
|
Node<T> next;
|
||||||
|
|
||||||
|
public static <T> Node<T> createNewNode(T data, Node<T> next) {
|
||||||
|
Node<T> node = new Node<T>();
|
||||||
|
node.data = data;
|
||||||
|
node.next = next;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> void traverseList(Node<T> root) {
|
||||||
|
if (root == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node<T> node = root;
|
||||||
|
while (node != null) {
|
||||||
|
System.out.println(node.data);
|
||||||
|
node = node.next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Node<T> getTail(Node<T> root) {
|
||||||
|
if (root == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node<T> node = root;
|
||||||
|
while (node.next != null) {
|
||||||
|
node = node.next;
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
package com.baeldung.algorithms.maze.solver;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class BFSMazeSolver {
|
||||||
|
private static final int[][] DIRECTIONS = { { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 } };
|
||||||
|
|
||||||
|
public List<Coordinate> solve(Maze maze) {
|
||||||
|
LinkedList<Coordinate> nextToVisit = new LinkedList<>();
|
||||||
|
Coordinate start = maze.getEntry();
|
||||||
|
nextToVisit.add(start);
|
||||||
|
|
||||||
|
while (!nextToVisit.isEmpty()) {
|
||||||
|
Coordinate cur = nextToVisit.remove();
|
||||||
|
|
||||||
|
if (!maze.isValidLocation(cur.getX(), cur.getY()) || maze.isExplored(cur.getX(), cur.getY())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maze.isWall(cur.getX(), cur.getY())) {
|
||||||
|
maze.setVisited(cur.getX(), cur.getY(), true);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maze.isExit(cur.getX(), cur.getY())) {
|
||||||
|
return backtrackPath(cur);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int[] direction : DIRECTIONS) {
|
||||||
|
Coordinate coordinate = new Coordinate(cur.getX() + direction[0], cur.getY() + direction[1], cur);
|
||||||
|
nextToVisit.add(coordinate);
|
||||||
|
maze.setVisited(cur.getX(), cur.getY(), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Coordinate> backtrackPath(Coordinate cur) {
|
||||||
|
List<Coordinate> path = new ArrayList<>();
|
||||||
|
Coordinate iter = cur;
|
||||||
|
|
||||||
|
while (iter != null) {
|
||||||
|
path.add(iter);
|
||||||
|
iter = iter.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package com.baeldung.algorithms.maze.solver;
|
||||||
|
|
||||||
|
public class Coordinate {
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
Coordinate parent;
|
||||||
|
|
||||||
|
public Coordinate(int x, int y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.parent = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Coordinate(int x, int y, Coordinate parent) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getX() {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getY() {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
Coordinate getParent() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package com.baeldung.algorithms.maze.solver;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class DFSMazeSolver {
|
||||||
|
private static final int[][] DIRECTIONS = { { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 } };
|
||||||
|
|
||||||
|
public List<Coordinate> solve(Maze maze) {
|
||||||
|
List<Coordinate> path = new ArrayList<>();
|
||||||
|
if (explore(maze, maze.getEntry()
|
||||||
|
.getX(),
|
||||||
|
maze.getEntry()
|
||||||
|
.getY(),
|
||||||
|
path)) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean explore(Maze maze, int row, int col, List<Coordinate> path) {
|
||||||
|
if (!maze.isValidLocation(row, col) || maze.isWall(row, col) || maze.isExplored(row, col)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
path.add(new Coordinate(row, col));
|
||||||
|
maze.setVisited(row, col, true);
|
||||||
|
|
||||||
|
if (maze.isExit(row, col)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int[] direction : DIRECTIONS) {
|
||||||
|
Coordinate coordinate = getNextCoordinate(row, col, direction[0], direction[1]);
|
||||||
|
if (explore(maze, coordinate.getX(), coordinate.getY(), path)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
path.remove(path.size() - 1);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Coordinate getNextCoordinate(int row, int col, int i, int j) {
|
||||||
|
return new Coordinate(row + i, col + j);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,141 @@
|
||||||
|
package com.baeldung.algorithms.maze.solver;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
|
public class Maze {
|
||||||
|
private static final int ROAD = 0;
|
||||||
|
private static final int WALL = 1;
|
||||||
|
private static final int START = 2;
|
||||||
|
private static final int EXIT = 3;
|
||||||
|
private static final int PATH = 4;
|
||||||
|
|
||||||
|
private int[][] maze;
|
||||||
|
private boolean[][] visited;
|
||||||
|
private Coordinate start;
|
||||||
|
private Coordinate end;
|
||||||
|
|
||||||
|
public Maze(File maze) throws FileNotFoundException {
|
||||||
|
String fileText = "";
|
||||||
|
try (Scanner input = new Scanner(maze)) {
|
||||||
|
while (input.hasNextLine()) {
|
||||||
|
fileText += input.nextLine() + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
initializeMaze(fileText);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeMaze(String text) {
|
||||||
|
if (text == null || (text = text.trim()).length() == 0) {
|
||||||
|
throw new IllegalArgumentException("empty lines data");
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] lines = text.split("[\r]?\n");
|
||||||
|
maze = new int[lines.length][lines[0].length()];
|
||||||
|
visited = new boolean[lines.length][lines[0].length()];
|
||||||
|
|
||||||
|
for (int row = 0; row < getHeight(); row++) {
|
||||||
|
if (lines[row].length() != getWidth()) {
|
||||||
|
throw new IllegalArgumentException("line " + (row + 1) + " wrong length (was " + lines[row].length() + " but should be " + getWidth() + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int col = 0; col < getWidth(); col++) {
|
||||||
|
if (lines[row].charAt(col) == '#')
|
||||||
|
maze[row][col] = WALL;
|
||||||
|
else if (lines[row].charAt(col) == 'S') {
|
||||||
|
maze[row][col] = START;
|
||||||
|
start = new Coordinate(row, col);
|
||||||
|
} else if (lines[row].charAt(col) == 'E') {
|
||||||
|
maze[row][col] = EXIT;
|
||||||
|
end = new Coordinate(row, col);
|
||||||
|
} else
|
||||||
|
maze[row][col] = ROAD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHeight() {
|
||||||
|
return maze.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getWidth() {
|
||||||
|
return maze[0].length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Coordinate getEntry() {
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Coordinate getExit() {
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isExit(int x, int y) {
|
||||||
|
return x == end.getX() && y == end.getY();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isStart(int x, int y) {
|
||||||
|
return x == start.getX() && y == start.getY();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isExplored(int row, int col) {
|
||||||
|
return visited[row][col];
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isWall(int row, int col) {
|
||||||
|
return maze[row][col] == WALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVisited(int row, int col, boolean value) {
|
||||||
|
visited[row][col] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isValidLocation(int row, int col) {
|
||||||
|
if (row < 0 || row >= getHeight() || col < 0 || col >= getWidth()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void printPath(List<Coordinate> path) {
|
||||||
|
int[][] tempMaze = Arrays.stream(maze)
|
||||||
|
.map(int[]::clone)
|
||||||
|
.toArray(int[][]::new);
|
||||||
|
for (Coordinate coordinate : path) {
|
||||||
|
if (isStart(coordinate.getX(), coordinate.getY()) || isExit(coordinate.getX(), coordinate.getY())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
tempMaze[coordinate.getX()][coordinate.getY()] = PATH;
|
||||||
|
}
|
||||||
|
System.out.println(toString(tempMaze));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString(int[][] maze) {
|
||||||
|
StringBuilder result = new StringBuilder(getWidth() * (getHeight() + 1));
|
||||||
|
for (int row = 0; row < getHeight(); row++) {
|
||||||
|
for (int col = 0; col < getWidth(); col++) {
|
||||||
|
if (maze[row][col] == ROAD) {
|
||||||
|
result.append(' ');
|
||||||
|
} else if (maze[row][col] == WALL) {
|
||||||
|
result.append('#');
|
||||||
|
} else if (maze[row][col] == START) {
|
||||||
|
result.append('S');
|
||||||
|
} else if (maze[row][col] == EXIT) {
|
||||||
|
result.append('E');
|
||||||
|
} else {
|
||||||
|
result.append('.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.append('\n');
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
for (int i = 0; i < visited.length; i++)
|
||||||
|
Arrays.fill(visited[i], false);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package com.baeldung.algorithms.maze.solver;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class MazeDriver {
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
File maze1 = new File("src/main/resources/maze/maze1.txt");
|
||||||
|
File maze2 = new File("src/main/resources/maze/maze2.txt");
|
||||||
|
|
||||||
|
execute(maze1);
|
||||||
|
execute(maze2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void execute(File file) throws Exception {
|
||||||
|
Maze maze = new Maze(file);
|
||||||
|
dfs(maze);
|
||||||
|
bfs(maze);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void bfs(Maze maze) {
|
||||||
|
BFSMazeSolver bfs = new BFSMazeSolver();
|
||||||
|
List<Coordinate> path = bfs.solve(maze);
|
||||||
|
maze.printPath(path);
|
||||||
|
maze.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void dfs(Maze maze) {
|
||||||
|
DFSMazeSolver dfs = new DFSMazeSolver();
|
||||||
|
List<Coordinate> path = dfs.solve(maze);
|
||||||
|
maze.printPath(path);
|
||||||
|
maze.reset();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
package com.baeldung.algorithms.numberwordconverter;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
import pl.allegro.finance.tradukisto.MoneyConverters;
|
||||||
|
|
||||||
|
public class NumberWordConverter {
|
||||||
|
|
||||||
|
public static final String INVALID_INPUT_GIVEN = "Invalid input given";
|
||||||
|
|
||||||
|
public static final String[] ones = { "", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" };
|
||||||
|
|
||||||
|
public static final String[] tens = {
|
||||||
|
"", // 0
|
||||||
|
"", // 1
|
||||||
|
"twenty", // 2
|
||||||
|
"thirty", // 3
|
||||||
|
"forty", // 4
|
||||||
|
"fifty", // 5
|
||||||
|
"sixty", // 6
|
||||||
|
"seventy", // 7
|
||||||
|
"eighty", // 8
|
||||||
|
"ninety" // 9
|
||||||
|
};
|
||||||
|
|
||||||
|
public static String getMoneyIntoWords(String input) {
|
||||||
|
MoneyConverters converter = MoneyConverters.ENGLISH_BANKING_MONEY_VALUE;
|
||||||
|
return converter.asWords(new BigDecimal(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getMoneyIntoWords(final double money) {
|
||||||
|
long dollar = (long) money;
|
||||||
|
long cents = Math.round((money - dollar) * 100);
|
||||||
|
if (money == 0D) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (money < 0) {
|
||||||
|
return INVALID_INPUT_GIVEN;
|
||||||
|
}
|
||||||
|
String dollarPart = "";
|
||||||
|
if (dollar > 0) {
|
||||||
|
dollarPart = convert(dollar) + " dollar" + (dollar == 1 ? "" : "s");
|
||||||
|
}
|
||||||
|
String centsPart = "";
|
||||||
|
if (cents > 0) {
|
||||||
|
if (dollarPart.length() > 0) {
|
||||||
|
centsPart = " and ";
|
||||||
|
}
|
||||||
|
centsPart += convert(cents) + " cent" + (cents == 1 ? "" : "s");
|
||||||
|
}
|
||||||
|
return dollarPart + centsPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String convert(final long n) {
|
||||||
|
if (n < 0) {
|
||||||
|
return INVALID_INPUT_GIVEN;
|
||||||
|
}
|
||||||
|
if (n < 20) {
|
||||||
|
return ones[(int) n];
|
||||||
|
}
|
||||||
|
if (n < 100) {
|
||||||
|
return tens[(int) n / 10] + ((n % 10 != 0) ? " " : "") + ones[(int) n % 10];
|
||||||
|
}
|
||||||
|
if (n < 1000) {
|
||||||
|
return ones[(int) n / 100] + " hundred" + ((n % 100 != 0) ? " " : "") + convert(n % 100);
|
||||||
|
}
|
||||||
|
if (n < 1_000_000) {
|
||||||
|
return convert(n / 1000) + " thousand" + ((n % 1000 != 0) ? " " : "") + convert(n % 1000);
|
||||||
|
}
|
||||||
|
if (n < 1_000_000_000) {
|
||||||
|
return convert(n / 1_000_000) + " million" + ((n % 1_000_000 != 0) ? " " : "") + convert(n % 1_000_000);
|
||||||
|
}
|
||||||
|
return convert(n / 1_000_000_000) + " billion" + ((n % 1_000_000_000 != 0) ? " " : "") + convert(n % 1_000_000_000);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package com.baeldung.algorithms.slope_one;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class InputData {
|
||||||
|
|
||||||
|
protected static List<Item> items = Arrays.asList(new Item("Candy"), new Item("Drink"), new Item("Soda"), new Item("Popcorn"), new Item("Snacks"));
|
||||||
|
|
||||||
|
public static Map<User, HashMap<Item, Double>> initializeData(int numberOfUsers) {
|
||||||
|
Map<User, HashMap<Item, Double>> data = new HashMap<>();
|
||||||
|
HashMap<Item, Double> newUser;
|
||||||
|
Set<Item> newRecommendationSet;
|
||||||
|
for (int i = 0; i < numberOfUsers; i++) {
|
||||||
|
newUser = new HashMap<Item, Double>();
|
||||||
|
newRecommendationSet = new HashSet<>();
|
||||||
|
for (int j = 0; j < 3; j++) {
|
||||||
|
newRecommendationSet.add(items.get((int) (Math.random() * 5)));
|
||||||
|
}
|
||||||
|
for (Item item : newRecommendationSet) {
|
||||||
|
newUser.put(item, Math.random());
|
||||||
|
}
|
||||||
|
data.put(new User("User " + i), newUser);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package com.baeldung.algorithms.slope_one;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class Item {
|
||||||
|
|
||||||
|
private String itemName;
|
||||||
|
}
|
|
@ -0,0 +1,124 @@
|
||||||
|
package com.baeldung.algorithms.slope_one;
|
||||||
|
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
import java.text.NumberFormat;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Slope One algorithm implementation
|
||||||
|
*/
|
||||||
|
public class SlopeOne {
|
||||||
|
|
||||||
|
private static Map<Item, Map<Item, Double>> diff = new HashMap<>();
|
||||||
|
private static Map<Item, Map<Item, Integer>> freq = new HashMap<>();
|
||||||
|
private static Map<User, HashMap<Item, Double>> inputData;
|
||||||
|
private static Map<User, HashMap<Item, Double>> outputData = new HashMap<>();
|
||||||
|
|
||||||
|
public static void slopeOne(int numberOfUsers) {
|
||||||
|
inputData = InputData.initializeData(numberOfUsers);
|
||||||
|
System.out.println("Slope One - Before the Prediction\n");
|
||||||
|
buildDifferencesMatrix(inputData);
|
||||||
|
System.out.println("\nSlope One - With Predictions\n");
|
||||||
|
predict(inputData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Based on the available data, calculate the relationships between the
|
||||||
|
* items and number of occurences
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* existing user data and their items' ratings
|
||||||
|
*/
|
||||||
|
private static void buildDifferencesMatrix(Map<User, HashMap<Item, Double>> data) {
|
||||||
|
for (HashMap<Item, Double> user : data.values()) {
|
||||||
|
for (Entry<Item, Double> e : user.entrySet()) {
|
||||||
|
if (!diff.containsKey(e.getKey())) {
|
||||||
|
diff.put(e.getKey(), new HashMap<Item, Double>());
|
||||||
|
freq.put(e.getKey(), new HashMap<Item, Integer>());
|
||||||
|
}
|
||||||
|
for (Entry<Item, Double> e2 : user.entrySet()) {
|
||||||
|
int oldCount = 0;
|
||||||
|
if (freq.get(e.getKey()).containsKey(e2.getKey())) {
|
||||||
|
oldCount = freq.get(e.getKey()).get(e2.getKey()).intValue();
|
||||||
|
}
|
||||||
|
double oldDiff = 0.0;
|
||||||
|
if (diff.get(e.getKey()).containsKey(e2.getKey())) {
|
||||||
|
oldDiff = diff.get(e.getKey()).get(e2.getKey()).doubleValue();
|
||||||
|
}
|
||||||
|
double observedDiff = e.getValue() - e2.getValue();
|
||||||
|
freq.get(e.getKey()).put(e2.getKey(), oldCount + 1);
|
||||||
|
diff.get(e.getKey()).put(e2.getKey(), oldDiff + observedDiff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Item j : diff.keySet()) {
|
||||||
|
for (Item i : diff.get(j).keySet()) {
|
||||||
|
double oldValue = diff.get(j).get(i).doubleValue();
|
||||||
|
int count = freq.get(j).get(i).intValue();
|
||||||
|
diff.get(j).put(i, oldValue / count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printData(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Based on existing data predict all missing ratings. If prediction is not
|
||||||
|
* possible, the value will be equal to -1
|
||||||
|
*
|
||||||
|
* @param data
|
||||||
|
* existing user data and their items' ratings
|
||||||
|
*/
|
||||||
|
private static void predict(Map<User, HashMap<Item, Double>> data) {
|
||||||
|
HashMap<Item, Double> uPred = new HashMap<Item, Double>();
|
||||||
|
HashMap<Item, Integer> uFreq = new HashMap<Item, Integer>();
|
||||||
|
for (Item j : diff.keySet()) {
|
||||||
|
uFreq.put(j, 0);
|
||||||
|
uPred.put(j, 0.0);
|
||||||
|
}
|
||||||
|
for (Entry<User, HashMap<Item, Double>> e : data.entrySet()) {
|
||||||
|
for (Item j : e.getValue().keySet()) {
|
||||||
|
for (Item k : diff.keySet()) {
|
||||||
|
try {
|
||||||
|
double predictedValue = diff.get(k).get(j).doubleValue() + e.getValue().get(j).doubleValue();
|
||||||
|
double finalValue = predictedValue * freq.get(k).get(j).intValue();
|
||||||
|
uPred.put(k, uPred.get(k) + finalValue);
|
||||||
|
uFreq.put(k, uFreq.get(k) + freq.get(k).get(j).intValue());
|
||||||
|
} catch (NullPointerException e1) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HashMap<Item, Double> clean = new HashMap<Item, Double>();
|
||||||
|
for (Item j : uPred.keySet()) {
|
||||||
|
if (uFreq.get(j) > 0) {
|
||||||
|
clean.put(j, uPred.get(j).doubleValue() / uFreq.get(j).intValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Item j : InputData.items) {
|
||||||
|
if (e.getValue().containsKey(j)) {
|
||||||
|
clean.put(j, e.getValue().get(j));
|
||||||
|
} else {
|
||||||
|
clean.put(j, -1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outputData.put(e.getKey(), clean);
|
||||||
|
}
|
||||||
|
printData(outputData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void printData(Map<User, HashMap<Item, Double>> data) {
|
||||||
|
for (User user : data.keySet()) {
|
||||||
|
System.out.println(user.getUsername() + ":");
|
||||||
|
print(data.get(user));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void print(HashMap<Item, Double> hashMap) {
|
||||||
|
NumberFormat formatter = new DecimalFormat("#0.000");
|
||||||
|
for (Item j : hashMap.keySet()) {
|
||||||
|
System.out.println(" " + j.getItemName() + " --> " + formatter.format(hashMap.get(j).doubleValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package com.baeldung.algorithms.slope_one;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class User {
|
||||||
|
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
package com.baeldung.algorithms.sudoku;
|
||||||
|
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
|
public class BacktrackingAlgorithm {
|
||||||
|
|
||||||
|
private static final int BOARD_SIZE = 9;
|
||||||
|
private static final int SUBSECTION_SIZE = 3;
|
||||||
|
private static final int BOARD_START_INDEX = 0;
|
||||||
|
|
||||||
|
private static final int NO_VALUE = 0;
|
||||||
|
private static final int MIN_VALUE = 1;
|
||||||
|
private static final int MAX_VALUE = 9;
|
||||||
|
|
||||||
|
private static int[][] board = {
|
||||||
|
{8, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
{0, 0, 3, 6, 0, 0, 0, 0, 0},
|
||||||
|
{0, 7, 0, 0, 9, 0, 2, 0, 0},
|
||||||
|
{0, 5, 0, 0, 0, 7, 0, 0, 0},
|
||||||
|
{0, 0, 0, 0, 4, 5, 7, 0, 0},
|
||||||
|
{0, 0, 0, 1, 0, 0, 0, 3, 0},
|
||||||
|
{0, 0, 1, 0, 0, 0, 0, 6, 8},
|
||||||
|
{0, 0, 8, 5, 0, 0, 0, 1, 0},
|
||||||
|
{0, 9, 0, 0, 0, 0, 4, 0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
BacktrackingAlgorithm solver = new BacktrackingAlgorithm();
|
||||||
|
solver.solve(board);
|
||||||
|
solver.printBoard();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printBoard() {
|
||||||
|
for (int row = BOARD_START_INDEX; row < BOARD_SIZE; row++) {
|
||||||
|
for (int column = BOARD_START_INDEX; column < BOARD_SIZE; column++) {
|
||||||
|
System.out.print(board[row][column] + " ");
|
||||||
|
}
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean solve(int[][] board) {
|
||||||
|
for (int row = BOARD_START_INDEX; row < BOARD_SIZE; row++) {
|
||||||
|
for (int column = BOARD_START_INDEX; column < BOARD_SIZE; column++) {
|
||||||
|
if (board[row][column] == NO_VALUE) {
|
||||||
|
for (int k = MIN_VALUE; k <= MAX_VALUE; k++) {
|
||||||
|
board[row][column] = k;
|
||||||
|
if (isValid(board, row, column) && solve(board)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
board[row][column] = NO_VALUE;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isValid(int[][] board, int row, int column) {
|
||||||
|
return rowConstraint(board, row) &&
|
||||||
|
columnConstraint(board, column) &&
|
||||||
|
subsectionConstraint(board, row, column);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean subsectionConstraint(int[][] board, int row, int column) {
|
||||||
|
boolean[] constraint = new boolean[BOARD_SIZE];
|
||||||
|
int subsectionRowStart = (row / SUBSECTION_SIZE) * SUBSECTION_SIZE;
|
||||||
|
int subsectionRowEnd = subsectionRowStart + SUBSECTION_SIZE;
|
||||||
|
|
||||||
|
int subsectionColumnStart = (column / SUBSECTION_SIZE) * SUBSECTION_SIZE;
|
||||||
|
int subsectionColumnEnd = subsectionColumnStart + SUBSECTION_SIZE;
|
||||||
|
|
||||||
|
for (int r = subsectionRowStart; r < subsectionRowEnd; r++) {
|
||||||
|
for (int c = subsectionColumnStart; c < subsectionColumnEnd; c++) {
|
||||||
|
if (!checkConstraint(board, r, constraint, c)) return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean columnConstraint(int[][] board, int column) {
|
||||||
|
boolean[] constraint = new boolean[BOARD_SIZE];
|
||||||
|
return IntStream.range(BOARD_START_INDEX, BOARD_SIZE)
|
||||||
|
.allMatch(row -> checkConstraint(board, row, constraint, column));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean rowConstraint(int[][] board, int row) {
|
||||||
|
boolean[] constraint = new boolean[BOARD_SIZE];
|
||||||
|
return IntStream.range(BOARD_START_INDEX, BOARD_SIZE)
|
||||||
|
.allMatch(column -> checkConstraint(board, row, constraint, column));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkConstraint(int[][] board, int row, boolean[] constraint, int column) {
|
||||||
|
if (board[row][column] != NO_VALUE) {
|
||||||
|
if (!constraint[board[row][column] - 1]) {
|
||||||
|
constraint[board[row][column] - 1] = true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.baeldung.algorithms.sudoku;
|
||||||
|
|
||||||
|
class ColumnNode extends DancingNode {
|
||||||
|
int size;
|
||||||
|
String name;
|
||||||
|
|
||||||
|
ColumnNode(String n) {
|
||||||
|
super();
|
||||||
|
size = 0;
|
||||||
|
name = n;
|
||||||
|
C = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cover() {
|
||||||
|
unlinkLR();
|
||||||
|
for (DancingNode i = this.D; i != this; i = i.D) {
|
||||||
|
for (DancingNode j = i.R; j != i; j = j.R) {
|
||||||
|
j.unlinkUD();
|
||||||
|
j.C.size--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void uncover() {
|
||||||
|
for (DancingNode i = this.U; i != this; i = i.U) {
|
||||||
|
for (DancingNode j = i.L; j != i; j = j.L) {
|
||||||
|
j.C.size++;
|
||||||
|
j.relinkUD();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
relinkLR();
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue