mirror of
https://github.com/kuhyx/WUT_Computer_Science.git
synced 2026-07-04 15:03:08 +02:00
tune up transaction generator
This commit is contained in:
parent
06f79923bb
commit
41abb20094
@ -8,63 +8,70 @@ import java.util.concurrent.ThreadLocalRandom;
|
|||||||
public class TransactionGenerator {
|
public class TransactionGenerator {
|
||||||
private static final int NUM_CARDS = 10000;
|
private static final int NUM_CARDS = 10000;
|
||||||
private static final int NUM_USERS = 5000; // Assuming each user has ~2 cards on average
|
private static final int NUM_USERS = 5000; // Assuming each user has ~2 cards on average
|
||||||
private static final Map<String, Set<Double[]>> cardLocations = new HashMap<>();
|
private static final Map<String, Set<LocationPoint>> cardLocations = new HashMap<>();
|
||||||
private static final Map<String, Double> cardLimits = new HashMap<>();
|
private static final Map<String, Double> cardLimits = new HashMap<>();
|
||||||
private static final Map<String, String> cardToUser = new HashMap<>();
|
private static final Map<String, String> cardToUser = new HashMap<>();
|
||||||
private static final List<String> cardIds = new ArrayList<>();
|
private static final List<String> cardIds = new ArrayList<>();
|
||||||
private static final List<String> userIds = new ArrayList<>();
|
private static final List<String> userIds = new ArrayList<>();
|
||||||
|
|
||||||
// Anomaly types
|
// Anomaly types
|
||||||
private static final int ANOMALY_NONE = 0;
|
private static final int ANOMALY_NONE = 0;
|
||||||
private static final int ANOMALY_AMOUNT = 1;
|
private static final int ANOMALY_AMOUNT = 1;
|
||||||
private static final int ANOMALY_LOCATION = 2;
|
private static final int ANOMALY_LOCATION = 2;
|
||||||
private static final int ANOMALY_FREQUENCY = 3;
|
private static final int ANOMALY_FREQUENCY = 3;
|
||||||
|
|
||||||
// Probability of generating an anomaly (1%)
|
// Probability of generating an anomaly (1%)
|
||||||
private static final double ANOMALY_PROBABILITY = 0.5;
|
private static final double ANOMALY_PROBABILITY = 0.5;
|
||||||
|
|
||||||
// Initialize card and user data
|
// Initialize card and user data
|
||||||
static {
|
static {
|
||||||
// Generate user IDs
|
// Generate user IDs
|
||||||
for (int i = 0; i < NUM_USERS; i++) {
|
for (int i = 0; i < NUM_USERS; i++) {
|
||||||
userIds.add("USER_" + String.format("%05d", i));
|
userIds.add("USER_" + String.format("%05d", i));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate card IDs and assign to users
|
// Generate card IDs and assign to users
|
||||||
for (int i = 0; i < NUM_CARDS; i++) {
|
for (int i = 0; i < NUM_CARDS; i++) {
|
||||||
String cardId = "CARD_" + String.format("%05d", i);
|
String cardId = "CARD_" + String.format("%05d", i);
|
||||||
cardIds.add(cardId);
|
cardIds.add(cardId);
|
||||||
String userId = userIds.get(ThreadLocalRandom.current().nextInt(NUM_USERS));
|
String userId = userIds.get(ThreadLocalRandom.current().nextInt(NUM_USERS));
|
||||||
cardToUser.put(cardId, userId);
|
cardToUser.put(cardId, userId);
|
||||||
|
|
||||||
// Initialize empty set of locations for this card
|
// Initialize empty set of locations for this card
|
||||||
cardLocations.put(cardId, new HashSet<>());
|
cardLocations.put(cardId, new HashSet<>());
|
||||||
|
|
||||||
// Assign random limit between $1,000 and $20,000
|
// Assign random limit between $1,000 and $20,000
|
||||||
cardLimits.put(cardId, 1000.0 + ThreadLocalRandom.current().nextDouble() * 19000.0);
|
cardLimits.put(cardId, 1000.0 + ThreadLocalRandom.current().nextDouble() * 19000.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Transaction generateTransaction(boolean forceAnomaly, int anomalyType) {
|
public Transaction generateTransaction(boolean forceAnomaly, int anomalyType) {
|
||||||
// Select a random card
|
// Select a random card
|
||||||
String cardId = cardIds.get(ThreadLocalRandom.current().nextInt(NUM_CARDS));
|
String cardId = cardIds.get(ThreadLocalRandom.current().nextInt(NUM_CARDS));
|
||||||
String userId = cardToUser.get(cardId);
|
String userId = cardToUser.get(cardId);
|
||||||
double availableLimit = cardLimits.get(cardId);
|
double availableLimit = cardLimits.get(cardId);
|
||||||
|
|
||||||
// Determine if this should be an anomaly
|
// Determine if this should be an anomaly
|
||||||
int actualAnomalyType = ANOMALY_NONE;
|
int actualAnomalyType = ANOMALY_NONE;
|
||||||
if (forceAnomaly) {
|
if (forceAnomaly) {
|
||||||
actualAnomalyType = (anomalyType >= 0 && anomalyType <= 3) ?
|
actualAnomalyType = (anomalyType >= 0 && anomalyType <= 3) ?
|
||||||
anomalyType : ThreadLocalRandom.current().nextInt(1, 4);
|
anomalyType : ThreadLocalRandom.current().nextInt(1, 4);
|
||||||
} else if (ThreadLocalRandom.current().nextDouble() < ANOMALY_PROBABILITY) {
|
} else if (ThreadLocalRandom.current().nextDouble() < ANOMALY_PROBABILITY) {
|
||||||
actualAnomalyType = ThreadLocalRandom.current().nextInt(1, 4);
|
double roll = ThreadLocalRandom.current().nextDouble();
|
||||||
|
if (roll < 0.3) {
|
||||||
|
actualAnomalyType = ANOMALY_LOCATION;
|
||||||
|
} else if (roll < 0.8) {
|
||||||
|
actualAnomalyType = ANOMALY_AMOUNT;
|
||||||
|
} else {
|
||||||
|
actualAnomalyType = ANOMALY_FREQUENCY;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get or generate location
|
// Get or generate location
|
||||||
Double[] location = getLocationForCard(cardId, actualAnomalyType == ANOMALY_LOCATION);
|
LocationPoint location = getLocationForCard(cardId, actualAnomalyType == ANOMALY_LOCATION);
|
||||||
double latitude = location[0];
|
double latitude = location.latitude;
|
||||||
double longitude = location[1];
|
double longitude = location.longitude;
|
||||||
|
|
||||||
// Generate transaction amount
|
// Generate transaction amount
|
||||||
double amount;
|
double amount;
|
||||||
if (actualAnomalyType == ANOMALY_AMOUNT) {
|
if (actualAnomalyType == ANOMALY_AMOUNT) {
|
||||||
@ -74,11 +81,11 @@ public class TransactionGenerator {
|
|||||||
// Normal amount (1-10% of available limit)
|
// Normal amount (1-10% of available limit)
|
||||||
amount = availableLimit * (0.01 + ThreadLocalRandom.current().nextDouble() * 0.09);
|
amount = availableLimit * (0.01 + ThreadLocalRandom.current().nextDouble() * 0.09);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update available limit
|
// Update available limit
|
||||||
double newLimit = availableLimit - amount;
|
double newLimit = availableLimit - amount;
|
||||||
cardLimits.put(cardId, newLimit > 0 ? newLimit : 0);
|
cardLimits.put(cardId, newLimit > 0 ? newLimit : 0);
|
||||||
|
|
||||||
// Create transaction
|
// Create transaction
|
||||||
Transaction transaction = new Transaction(
|
Transaction transaction = new Transaction(
|
||||||
cardId,
|
cardId,
|
||||||
@ -89,56 +96,56 @@ public class TransactionGenerator {
|
|||||||
newLimit,
|
newLimit,
|
||||||
Instant.now()
|
Instant.now()
|
||||||
);
|
);
|
||||||
|
|
||||||
return transaction;
|
return transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Double[] getLocationForCard(String cardId, boolean generateAnomaly) {
|
private LocationPoint getLocationForCard(String cardId, boolean generateAnomaly) {
|
||||||
Set<Double[]> locations = cardLocations.get(cardId);
|
Set<LocationPoint> locations = cardLocations.get(cardId);
|
||||||
|
|
||||||
if (locations.isEmpty() || generateAnomaly) {
|
if (locations.isEmpty() || generateAnomaly) {
|
||||||
// Generate a random worldwide location
|
// Generate a random worldwide location
|
||||||
double latitude = ThreadLocalRandom.current().nextDouble(-90, 90);
|
double latitude = ThreadLocalRandom.current().nextDouble(-90, 90);
|
||||||
double longitude = ThreadLocalRandom.current().nextDouble(-180, 180);
|
double longitude = ThreadLocalRandom.current().nextDouble(-180, 180);
|
||||||
Double[] newLocation = {latitude, longitude};
|
LocationPoint newLocation = new LocationPoint(latitude, longitude);
|
||||||
|
|
||||||
// Store this location for future use unless it's an anomaly
|
// Store this location for future use unless it's an anomaly
|
||||||
if (!generateAnomaly) {
|
if (!generateAnomaly) {
|
||||||
locations.add(newLocation);
|
locations.add(newLocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
return newLocation;
|
return newLocation;
|
||||||
} else {
|
} else {
|
||||||
// Pick a random location from the card's history
|
// Pick a random location from the card's history
|
||||||
Double[][] locArray = locations.toArray(new Double[0][]);
|
LocationPoint[] locArray = locations.toArray(new LocationPoint[0]);
|
||||||
return locArray[ThreadLocalRandom.current().nextInt(locArray.length)];
|
return locArray[ThreadLocalRandom.current().nextInt(locArray.length)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method to simulate frequency anomaly by generating multiple transactions in short succession
|
// Method to simulate frequency anomaly by generating multiple transactions in short succession
|
||||||
public List<Transaction> generateFrequencyAnomaly(String specificCardId) {
|
public List<Transaction> generateFrequencyAnomaly(String specificCardId) {
|
||||||
List<Transaction> transactions = new ArrayList<>();
|
List<Transaction> transactions = new ArrayList<>();
|
||||||
String cardId = specificCardId != null ?
|
String cardId = specificCardId != null ?
|
||||||
specificCardId : cardIds.get(ThreadLocalRandom.current().nextInt(NUM_CARDS));
|
specificCardId : cardIds.get(ThreadLocalRandom.current().nextInt(NUM_CARDS));
|
||||||
|
|
||||||
// Generate 5-10 transactions in quick succession
|
// Generate 5-10 transactions in quick succession
|
||||||
int numTransactions = ThreadLocalRandom.current().nextInt(5, 11);
|
int numTransactions = ThreadLocalRandom.current().nextInt(5, 11);
|
||||||
for (int i = 0; i < numTransactions; i++) {
|
for (int i = 0; i < numTransactions; i++) {
|
||||||
transactions.add(generateTransactionForCard(cardId, false, ANOMALY_NONE));
|
transactions.add(generateTransactionForCard(cardId, false, ANOMALY_NONE));
|
||||||
}
|
}
|
||||||
|
|
||||||
return transactions;
|
return transactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Transaction generateTransactionForCard(String cardId, boolean forceAnomaly, int anomalyType) {
|
private Transaction generateTransactionForCard(String cardId, boolean forceAnomaly, int anomalyType) {
|
||||||
String userId = cardToUser.get(cardId);
|
String userId = cardToUser.get(cardId);
|
||||||
double availableLimit = cardLimits.get(cardId);
|
double availableLimit = cardLimits.get(cardId);
|
||||||
|
|
||||||
// Get location
|
// Get location
|
||||||
Double[] location = getLocationForCard(cardId, forceAnomaly && anomalyType == ANOMALY_LOCATION);
|
LocationPoint location = getLocationForCard(cardId, forceAnomaly && anomalyType == ANOMALY_LOCATION);
|
||||||
double latitude = location[0];
|
double latitude = location.latitude;
|
||||||
double longitude = location[1];
|
double longitude = location.longitude;
|
||||||
|
|
||||||
// Generate amount
|
// Generate amount
|
||||||
double amount;
|
double amount;
|
||||||
if (forceAnomaly && anomalyType == ANOMALY_AMOUNT) {
|
if (forceAnomaly && anomalyType == ANOMALY_AMOUNT) {
|
||||||
@ -146,11 +153,11 @@ public class TransactionGenerator {
|
|||||||
} else {
|
} else {
|
||||||
amount = availableLimit * (0.01 + ThreadLocalRandom.current().nextDouble() * 0.09);
|
amount = availableLimit * (0.01 + ThreadLocalRandom.current().nextDouble() * 0.09);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update available limit
|
// Update available limit
|
||||||
double newLimit = availableLimit - amount;
|
double newLimit = availableLimit - amount;
|
||||||
cardLimits.put(cardId, newLimit > 0 ? newLimit : 0);
|
cardLimits.put(cardId, newLimit > 0 ? newLimit : 0);
|
||||||
|
|
||||||
return new Transaction(
|
return new Transaction(
|
||||||
cardId,
|
cardId,
|
||||||
userId,
|
userId,
|
||||||
@ -161,4 +168,28 @@ public class TransactionGenerator {
|
|||||||
Instant.now()
|
Instant.now()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class LocationPoint {
|
||||||
|
final double latitude;
|
||||||
|
final double longitude;
|
||||||
|
|
||||||
|
LocationPoint(double latitude, double longitude) {
|
||||||
|
this.latitude = latitude;
|
||||||
|
this.longitude = longitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
LocationPoint that = (LocationPoint) o;
|
||||||
|
return Double.compare(that.latitude, latitude) == 0 &&
|
||||||
|
Double.compare(that.longitude, longitude) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(latitude, longitude);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user